]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 21 Sep 2010 19:49:14 +0000 (15:49 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 21 Sep 2010 19:49:14 +0000 (15:49 -0400)
Conflicts:
arch/arm/mach-omap2/board-omap3pandora.c
drivers/net/wireless/ath/ath5k/base.c

155 files changed:
MAINTAINERS
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/board-zoom-peripherals.c
drivers/net/wireless/Makefile
drivers/net/wireless/airo.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/attach.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/debug.c
drivers/net/wireless/ath/ath5k/pcu.c
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath5k/qcu.c
drivers/net/wireless/ath/ath5k/reg.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9002_phy.h
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/eeprom.h
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/eeprom_9287.c
drivers/net/wireless/ath/ath9k/eeprom_def.c
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/phy.h
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/ath/ath9k/rc.h
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/virtual.c
drivers/net/wireless/ath/ath9k/wmi.c
drivers/net/wireless/ath/ath9k/wmi.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/carl9170.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/cmd.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/cmd.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/debug.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/eeprom.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/fw.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/fwcmd.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/fwdesc.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/hw.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/led.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/mac.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/main.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/phy.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/phy.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/rx.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/tx.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/usb.c [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/version.h [new file with mode: 0644]
drivers/net/wireless/ath/carl9170/wlan.h [new file with mode: 0644]
drivers/net/wireless/ath/key.c [new file with mode: 0644]
drivers/net/wireless/ath/reg.h
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-3945.h
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn-debugfs.c
drivers/net/wireless/iwlwifi/iwl-agn-debugfs.h
drivers/net/wireless/iwlwifi/iwl-agn-hcmd.c
drivers/net/wireless/iwlwifi/iwl-agn-lib.c
drivers/net/wireless/iwlwifi/iwl-agn-rs.h
drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-agn.h
drivers/net/wireless/iwlwifi/iwl-commands.h
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-debugfs.c
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-scan.c
drivers/net/wireless/iwlwifi/iwl-sta.c
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/mesh.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/p54/p54spi.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800lib.h
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00config.c
drivers/net/wireless/rt2x00/rt2x00lib.h
drivers/net/wireless/rt2x00/rt2x00link.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/wl12xx/Kconfig
drivers/net/wireless/wl12xx/wl1251_sdio.c
drivers/net/wireless/wl12xx/wl1251_spi.c
drivers/net/wireless/wl12xx/wl1271.h
drivers/net/wireless/wl12xx/wl1271_boot.c
drivers/net/wireless/wl12xx/wl1271_boot.h
drivers/net/wireless/wl12xx/wl1271_io.h
drivers/net/wireless/wl12xx/wl1271_main.c
drivers/net/wireless/wl12xx/wl1271_rx.c
drivers/net/wireless/wl12xx/wl1271_sdio.c
drivers/net/wireless/wl12xx/wl1271_spi.c
drivers/net/wireless/wl12xx/wl12xx_platform_data.c [new file with mode: 0644]
include/linux/nl80211.h
include/linux/wl12xx.h [moved from include/linux/spi/wl12xx.h with 82% similarity]
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/radiotap.c
net/wireless/reg.c
net/wireless/sme.c
net/wireless/util.c

index 102352e6d61ddba67a4162423eca11f4e429dc9b..017bf493166e7877b1e8607324be674c9737efe2 100644 (file)
@@ -1120,6 +1120,13 @@ W:       http://wireless.kernel.org/en/users/Drivers/ar9170
 S:     Maintained
 F:     drivers/net/wireless/ath/ar9170/
 
+CARL9170 LINUX COMMUNITY WIRELESS DRIVER
+M:     Christian Lamparter <chunkeey@googlemail.com>
+L:     linux-wireless@vger.kernel.org
+W:     http://wireless.kernel.org/en/users/Drivers/carl9170
+S:     Maintained
+F:     drivers/net/wireless/ath/carl9170/
+
 ATK0110 HWMON DRIVER
 M:     Luca Tettamanti <kronos.it@gmail.com>
 L:     lm-sensors@lm-sensors.org
@@ -6427,7 +6434,7 @@ W:        http://wireless.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:     Maintained
 F:     drivers/net/wireless/wl12xx/wl1271*
-F:     include/linux/spi/wl12xx.h
+F:     include/linux/wl12xx.h
 
 WL3501 WIRELESS PCMCIA CARD DRIVER
 M:     Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
index dd3af2be13be2a79e14a52360088c8cbb9db575b..7ea1eb4a26b46484b52fc959a8dbcfca30f27f43 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/spi/ads7846.h>
 #include <linux/regulator/machine.h>
 #include <linux/i2c/twl.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 #include <linux/leds.h>
index 9a5eb87425fcf91dc3164acf3b1386e38e655be7..ce28a851dcd3f06f954ccd64a03e72c30e229854 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/input.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/i2c.h>
 #include <linux/i2c/twl.h>
 #include <linux/clk.h>
index 6b3984964cc59696a98b21872f5f768eccac71d6..6aa0728fa15dcd5ec301cf4a379080861c60bfe9 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/gpio.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/wl12xx.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -27,6 +29,9 @@
 #include "mux.h"
 #include "hsmmc.h"
 
+#define OMAP_ZOOM_WLAN_PMENA_GPIO      (101)
+#define OMAP_ZOOM_WLAN_IRQ_GPIO                (162)
+
 /* Zoom2 has Qwerty keyboard*/
 static int board_keymap[] = {
        KEY(0, 0, KEY_E),
@@ -106,6 +111,11 @@ static struct regulator_consumer_supply zoom_vmmc2_supply = {
        .supply         = "vmmc",
 };
 
+static struct regulator_consumer_supply zoom_vmmc3_supply = {
+       .supply         = "vmmc",
+       .dev_name       = "mmci-omap-hs.2",
+};
+
 /* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */
 static struct regulator_init_data zoom_vmmc1 = {
        .constraints = {
@@ -151,6 +161,38 @@ static struct regulator_init_data zoom_vsim = {
        .consumer_supplies      = &zoom_vsim_supply,
 };
 
+static struct regulator_init_data zoom_vmmc3 = {
+       .constraints = {
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies = &zoom_vmmc3_supply,
+};
+
+static struct fixed_voltage_config zoom_vwlan = {
+       .supply_name            = "vwl1271",
+       .microvolts             = 1800000, /* 1.8V */
+       .gpio                   = OMAP_ZOOM_WLAN_PMENA_GPIO,
+       .startup_delay          = 70000, /* 70msec */
+       .enable_high            = 1,
+       .enabled_at_boot        = 0,
+       .init_data              = &zoom_vmmc3,
+};
+
+static struct platform_device omap_vwlan_device = {
+       .name           = "reg-fixed-voltage",
+       .id             = 1,
+       .dev = {
+               .platform_data  = &zoom_vwlan,
+       },
+};
+
+struct wl12xx_platform_data omap_zoom_wlan_data __initdata = {
+       .irq = OMAP_GPIO_IRQ(OMAP_ZOOM_WLAN_IRQ_GPIO),
+       /* ZOOM ref clock is 26 MHz */
+       .board_ref_clock = 1,
+};
+
 static struct omap2_hsmmc_info mmc[] __initdata = {
        {
                .name           = "external",
@@ -168,6 +210,14 @@ static struct omap2_hsmmc_info mmc[] __initdata = {
                .nonremovable   = true,
                .power_saving   = true,
        },
+       {
+               .name           = "wl1271",
+               .mmc            = 3,
+               .wires          = 4,
+               .gpio_wp        = -EINVAL,
+               .gpio_cd        = -EINVAL,
+               .nonremovable   = true,
+       },
        {}      /* Terminator */
 };
 
@@ -279,7 +329,11 @@ static void enable_board_wakeup_source(void)
 
 void __init zoom_peripherals_init(void)
 {
+       if (wl12xx_set_platform_data(&omap_zoom_wlan_data))
+               pr_err("error setting wl12xx data\n");
+
        omap_i2c_init();
+       platform_device_register(&omap_vwlan_device);
        usb_musb_init(&musb_board_data);
        enable_board_wakeup_source();
 }
index 5d4ce4d2b32bd9a71a63a1b6c25606f647b3d0bc..85af697574a6e6211733a334d0c49122bbc68b9b 100644 (file)
@@ -50,5 +50,7 @@ obj-$(CONFIG_ATH_COMMON)      += ath/
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
 
 obj-$(CONFIG_WL12XX)   += wl12xx/
+# small builtin driver bit
+obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wl12xx/wl12xx_platform_data.o
 
 obj-$(CONFIG_IWM)      += iwmc3200wifi/
index 7d26506957d7e167a0d3e72790f6420c582c89cc..924ed095dd997116d8e334c7e7f81288eec34aeb 100644 (file)
@@ -105,7 +105,7 @@ static struct pci_driver airo_driver = {
    of statistics in the /proc filesystem */
 
 #define IGNLABEL(comment) NULL
-static char *statsLabels[] = {
+static const char *statsLabels[] = {
        "RxOverrun",
        IGNLABEL("RxPlcpCrcErr"),
        IGNLABEL("RxPlcpFormatErr"),
@@ -932,7 +932,7 @@ typedef struct aironet_ioctl {
        unsigned char __user *data;     // d-data
 } aironet_ioctl;
 
-static char swversion[] = "2.1";
+static const char swversion[] = "2.1";
 #endif /* CISCO_EXT */
 
 #define NUM_MODULES       2
@@ -1374,7 +1374,7 @@ static int micsetup(struct airo_info *ai) {
        return SUCCESS;
 }
 
-static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
 
 /*===========================================================================
  * Description: Mic a packet
@@ -5023,7 +5023,7 @@ static void proc_config_on_close(struct inode *inode, struct file *file)
        airo_config_commit(dev, NULL, NULL, NULL);
 }
 
-static char *get_rmode(__le16 mode)
+static const char *get_rmode(__le16 mode)
 {
         switch(mode & RXMODE_MASK) {
         case RXMODE_RFMON:  return "rfmon";
index 0a75be027afaa5c7604cfc257093979a636653ab..92c216263ee9d7304745f934c82e14dc3059f21b 100644 (file)
@@ -25,5 +25,6 @@ config ATH_DEBUG
 source "drivers/net/wireless/ath/ath5k/Kconfig"
 source "drivers/net/wireless/ath/ath9k/Kconfig"
 source "drivers/net/wireless/ath/ar9170/Kconfig"
+source "drivers/net/wireless/ath/carl9170/Kconfig"
 
 endif
index 8113a5042afac6e99904ab668b6a4491f901a363..6d711ec97ec2fd48f3f69d31b441dffc545821c4 100644 (file)
@@ -1,11 +1,13 @@
 obj-$(CONFIG_ATH5K)            += ath5k/
 obj-$(CONFIG_ATH9K_HW)         += ath9k/
 obj-$(CONFIG_AR9170_USB)        += ar9170/
+obj-$(CONFIG_CARL9170)         += carl9170/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
 ath-objs :=    main.o \
                regd.o \
-               hw.o
+               hw.o \
+               key.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
index a706202fa67c9360f70e3fc6ec2351b8f68e943f..dd236c3b52f6218755b2197845f987a487d87a43 100644 (file)
@@ -71,6 +71,32 @@ struct ath_regulatory {
        struct reg_dmn_pair_mapping *regpair;
 };
 
+enum ath_crypt_caps {
+       ATH_CRYPT_CAP_CIPHER_AESCCM             = BIT(0),
+       ATH_CRYPT_CAP_MIC_COMBINED              = BIT(1),
+};
+
+struct ath_keyval {
+       u8 kv_type;
+       u8 kv_pad;
+       u16 kv_len;
+       u8 kv_val[16]; /* TK */
+       u8 kv_mic[8]; /* Michael MIC key */
+       u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
+                        * supports both MIC keys in the same key cache entry;
+                        * in that case, kv_mic is the RX key) */
+};
+
+enum ath_cipher {
+       ATH_CIPHER_WEP = 0,
+       ATH_CIPHER_AES_OCB = 1,
+       ATH_CIPHER_AES_CCM = 2,
+       ATH_CIPHER_CKIP = 3,
+       ATH_CIPHER_TKIP = 4,
+       ATH_CIPHER_CLR = 5,
+       ATH_CIPHER_MIC = 127
+};
+
 /**
  * struct ath_ops - Register read/write operations
  *
@@ -120,7 +146,7 @@ struct ath_common {
        u32 keymax;
        DECLARE_BITMAP(keymap, ATH_KEYMAX);
        DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX);
-       u8 splitmic;
+       enum ath_crypt_caps crypt_caps;
 
        struct ath_regulatory regulatory;
        const struct ath_ops *ops;
@@ -132,5 +158,11 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
                                gfp_t gfp_mask);
 
 void ath_hw_setbssidmask(struct ath_common *common);
+void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key);
+int ath_key_config(struct ath_common *common,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key);
+bool ath_hw_keyreset(struct ath_common *common, u16 entry);
 
 #endif /* ATH_H */
index f399c4dd8e6980297a409005ebc8f786109bf816..b96bb985b56deca0fa191db1f7a980051d4b7729 100644 (file)
 #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI    1000    /* 1 sec */
 #define ATH5K_TUNE_CALIBRATION_INTERVAL_NF     60000   /* 60 sec */
 
+#define ATH5K_TX_COMPLETE_POLL_INT             3000    /* 3 sec */
+
 #define AR5K_INIT_CARR_SENSE_EN                        1
 
 /*Swap RX/TX Descriptor for big endian archs*/
        (AR5K_INIT_PROG_IFS_TURBO)                                      \
 )
 
-/* token to use for aifs, cwmin, cwmax in MadWiFi */
-#define        AR5K_TXQ_USEDEFAULT     ((u32) -1)
 
 /* GENERIC CHIPSET DEFINITIONS */
 
@@ -528,9 +528,9 @@ 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 */
+       u     tqi_aifs;       /* Arbitrated Interframe Space */
+       u16     tqi_cw_min;     /* Minimum Contention Window */
+       u16     tqi_cw_max;     /* Maximum Contention Window */
        u32     tqi_cbr_period; /* Constant bit rate period */
        u32     tqi_cbr_overflow_limit;
        u32     tqi_burst_time;
@@ -1028,8 +1028,6 @@ struct ath5k_hw {
        bool                    ah_turbo;
        bool                    ah_calibration;
        bool                    ah_single_chip;
-       bool                    ah_aes_support;
-       bool                    ah_combined_mic;
 
        enum ath5k_version      ah_version;
        enum ath5k_radio        ah_radio;
@@ -1044,9 +1042,6 @@ struct ath5k_hw {
 #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;
        u32                     ah_limit_tx_retries;
        u8                      ah_coverage_class;
 
@@ -1207,11 +1202,6 @@ void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high);
 unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec);
 unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock);
 unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah);
-/* Key table (WEP) functions */
-int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry);
-int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
-                    const struct ieee80211_key_conf *key, const u8 *mac);
-int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac);
 
 /* Queue Control Unit, DFS Control Unit Functions */
 int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
index aabad4f13e2a47d3f826a5848f164d99f255b793..6e02de311cdd651503109ea82e43f6b2e6a464ad 100644 (file)
@@ -119,8 +119,6 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
        ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
        ah->ah_imr = 0;
        ah->ah_atim_window = 0;
-       ah->ah_aifs = AR5K_TUNE_AIFS;
-       ah->ah_cw_min = AR5K_TUNE_CWMIN;
        ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
        ah->ah_software_retry = false;
        ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
@@ -314,12 +312,16 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
        }
 
        /* Crypto settings */
-       ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 &&
-               (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
-                !AR5K_EEPROM_AES_DIS(ee->ee_misc5));
+       common->keymax = (sc->ah->ah_version == AR5K_AR5210 ?
+                         AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211);
+
+       if (srev >= AR5K_SREV_AR5212_V4 &&
+           (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
+           !AR5K_EEPROM_AES_DIS(ee->ee_misc5)))
+               common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
 
        if (srev >= AR5K_SREV_AR2414) {
-               ah->ah_combined_mic = true;
+               common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
                AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
                        AR5K_MISC_MODE_COMBINED_MIC);
        }
index 116ac66c6e3ecd60d19800a46af184926cfafc76..95072db0ec21086b00f88092727691c90ac16b0d 100644 (file)
@@ -70,11 +70,6 @@ static int modparam_all_channels;
 module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
 MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
 
-
-/******************\
-* Internal defines *
-\******************/
-
 /* Module info */
 MODULE_AUTHOR("Jiri Slaby");
 MODULE_AUTHOR("Nick Kossifidis");
@@ -83,6 +78,10 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
 
+static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
+static int ath5k_beacon_update(struct ieee80211_hw *hw,
+               struct ieee80211_vif *vif);
+static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
 
 /* Known PCI ids */
 static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
@@ -190,129 +189,6 @@ static const struct ieee80211_rate ath5k_rates[] = {
        /* XR missing */
 };
 
-/*
- * Prototypes - PCI stack related functions
- */
-static int __devinit   ath5k_pci_probe(struct pci_dev *pdev,
-                               const struct pci_device_id *id);
-static void __devexit  ath5k_pci_remove(struct pci_dev *pdev);
-#ifdef CONFIG_PM_SLEEP
-static int             ath5k_pci_suspend(struct device *dev);
-static int             ath5k_pci_resume(struct device *dev);
-
-static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
-#define ATH5K_PM_OPS   (&ath5k_pm_ops)
-#else
-#define ATH5K_PM_OPS   NULL
-#endif /* CONFIG_PM_SLEEP */
-
-static struct pci_driver ath5k_pci_driver = {
-       .name           = KBUILD_MODNAME,
-       .id_table       = ath5k_pci_id_table,
-       .probe          = ath5k_pci_probe,
-       .remove         = __devexit_p(ath5k_pci_remove),
-       .driver.pm      = ATH5K_PM_OPS,
-};
-
-
-
-/*
- * Prototypes - MAC 802.11 stack related functions
- */
-static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-               struct ath5k_txq *txq);
-static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
-static int ath5k_start(struct ieee80211_hw *hw);
-static void ath5k_stop(struct ieee80211_hw *hw);
-static int ath5k_add_interface(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif);
-static void ath5k_remove_interface(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif);
-static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
-static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
-                                  struct netdev_hw_addr_list *mc_list);
-static void ath5k_configure_filter(struct ieee80211_hw *hw,
-               unsigned int changed_flags,
-               unsigned int *new_flags,
-               u64 multicast);
-static int ath5k_set_key(struct ieee80211_hw *hw,
-               enum set_key_cmd cmd,
-               struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-               struct ieee80211_key_conf *key);
-static int ath5k_get_stats(struct ieee80211_hw *hw,
-               struct ieee80211_low_level_stats *stats);
-static int ath5k_get_survey(struct ieee80211_hw *hw,
-               int idx, struct survey_info *survey);
-static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
-static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
-static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif);
-static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif,
-               struct ieee80211_bss_conf *bss_conf,
-               u32 changes);
-static void ath5k_sw_scan_start(struct ieee80211_hw *hw);
-static void ath5k_sw_scan_complete(struct ieee80211_hw *hw);
-static void ath5k_set_coverage_class(struct ieee80211_hw *hw,
-               u8 coverage_class);
-
-static const struct ieee80211_ops ath5k_hw_ops = {
-       .tx             = ath5k_tx,
-       .start          = ath5k_start,
-       .stop           = ath5k_stop,
-       .add_interface  = ath5k_add_interface,
-       .remove_interface = ath5k_remove_interface,
-       .config         = ath5k_config,
-       .prepare_multicast = ath5k_prepare_multicast,
-       .configure_filter = ath5k_configure_filter,
-       .set_key        = ath5k_set_key,
-       .get_stats      = ath5k_get_stats,
-       .get_survey     = ath5k_get_survey,
-       .conf_tx        = NULL,
-       .get_tsf        = ath5k_get_tsf,
-       .set_tsf        = ath5k_set_tsf,
-       .reset_tsf      = ath5k_reset_tsf,
-       .bss_info_changed = ath5k_bss_info_changed,
-       .sw_scan_start  = ath5k_sw_scan_start,
-       .sw_scan_complete = ath5k_sw_scan_complete,
-       .set_coverage_class = ath5k_set_coverage_class,
-};
-
-/*
- * Prototypes - Internal functions
- */
-/* Attach detach */
-static int     ath5k_attach(struct pci_dev *pdev,
-                       struct ieee80211_hw *hw);
-static void    ath5k_detach(struct pci_dev *pdev,
-                       struct ieee80211_hw *hw);
-/* Channel/mode setup */
-static inline short ath5k_ieee2mhz(short chan);
-static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
-                               struct ieee80211_channel *channels,
-                               unsigned int mode,
-                               unsigned int max);
-static int     ath5k_setup_bands(struct ieee80211_hw *hw);
-static int     ath5k_chan_set(struct ath5k_softc *sc,
-                               struct ieee80211_channel *chan);
-static void    ath5k_setcurmode(struct ath5k_softc *sc,
-                               unsigned int mode);
-static void    ath5k_mode_setup(struct ath5k_softc *sc);
-
-/* Descriptor setup */
-static int     ath5k_desc_alloc(struct ath5k_softc *sc,
-                               struct pci_dev *pdev);
-static void    ath5k_desc_free(struct ath5k_softc *sc,
-                               struct pci_dev *pdev);
-/* Buffers setup */
-static int     ath5k_rxbuf_setup(struct ath5k_softc *sc,
-                               struct ath5k_buf *bf);
-static int     ath5k_txbuf_setup(struct ath5k_softc *sc,
-                               struct ath5k_buf *bf,
-                               struct ath5k_txq *txq, int padsize);
-
 static inline void ath5k_txbuf_free_skb(struct ath5k_softc *sc,
                                struct ath5k_buf *bf)
 {
@@ -345,35 +221,6 @@ static inline void ath5k_rxbuf_free_skb(struct ath5k_softc *sc,
 }
 
 
-/* Queues setup */
-static struct  ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc,
-                               int qtype, int subtype);
-static int     ath5k_beaconq_setup(struct ath5k_hw *ah);
-static int     ath5k_beaconq_config(struct ath5k_softc *sc);
-static void    ath5k_txq_drainq(struct ath5k_softc *sc,
-                               struct ath5k_txq *txq);
-static void    ath5k_txq_cleanup(struct ath5k_softc *sc);
-static void    ath5k_txq_release(struct ath5k_softc *sc);
-/* Rx handling */
-static int     ath5k_rx_start(struct ath5k_softc *sc);
-static void    ath5k_rx_stop(struct ath5k_softc *sc);
-static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
-                                       struct sk_buff *skb,
-                                       struct ath5k_rx_status *rs);
-static void    ath5k_tasklet_rx(unsigned long data);
-/* Tx handling */
-static void    ath5k_tx_processq(struct ath5k_softc *sc,
-                               struct ath5k_txq *txq);
-static void    ath5k_tasklet_tx(unsigned long data);
-/* Beacon handling */
-static int     ath5k_beacon_setup(struct ath5k_softc *sc,
-                                       struct ath5k_buf *bf);
-static void    ath5k_beacon_send(struct ath5k_softc *sc);
-static void    ath5k_beacon_config(struct ath5k_softc *sc);
-static void    ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
-static void    ath5k_tasklet_beacon(unsigned long data);
-static void    ath5k_tasklet_ani(unsigned long data);
-
 static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 {
        u64 tsf = ath5k_hw_get_tsf64(ah);
@@ -384,50 +231,6 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
        return (tsf & ~0x7fff) | rstamp;
 }
 
-/* Interrupt handling */
-static int     ath5k_init(struct ath5k_softc *sc);
-static int     ath5k_stop_locked(struct ath5k_softc *sc);
-static int     ath5k_stop_hw(struct ath5k_softc *sc);
-static irqreturn_t ath5k_intr(int irq, void *dev_id);
-static void ath5k_reset_work(struct work_struct *work);
-
-static void    ath5k_tasklet_calibrate(unsigned long data);
-
-/*
- * Module init/exit functions
- */
-static int __init
-init_ath5k_pci(void)
-{
-       int ret;
-
-       ath5k_debug_init();
-
-       ret = pci_register_driver(&ath5k_pci_driver);
-       if (ret) {
-               printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static void __exit
-exit_ath5k_pci(void)
-{
-       pci_unregister_driver(&ath5k_pci_driver);
-
-       ath5k_debug_finish();
-}
-
-module_init(init_ath5k_pci);
-module_exit(exit_ath5k_pci);
-
-
-/********************\
-* PCI Initialization *
-\********************/
-
 static const char *
 ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
 {
@@ -466,3082 +269,3276 @@ static const struct ath_ops ath5k_common_ops = {
        .write = ath5k_iowrite32,
 };
 
-static int __devinit
-ath5k_pci_probe(struct pci_dev *pdev,
-               const struct pci_device_id *id)
-{
-       void __iomem *mem;
-       struct ath5k_softc *sc;
-       struct ath_common *common;
-       struct ieee80211_hw *hw;
-       int ret;
-       u8 csz;
+/***********************\
+* Driver Initialization *
+\***********************/
 
-       /*
-        * L0s needs to be disabled on all ath5k cards.
-        *
-        * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
-        * by default in the future in 2.6.36) this will also mean both L1 and
-        * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
-        * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
-        * though but cannot currently undue the effect of a blacklist, for
-        * details you can read pcie_aspm_sanity_check() and see how it adjusts
-        * the device link capability.
-        *
-        * It may be possible in the future to implement some PCI API to allow
-        * drivers to override blacklists for pre 1.1 PCIe but for now it is
-        * best to accept that both L0s and L1 will be disabled completely for
-        * distributions shipping with CONFIG_PCIEASPM rather than having this
-        * issue present. Motivation for adding this new API will be to help
-        * with power consumption for some of these devices.
-        */
-       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
+static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ath5k_softc *sc = hw->priv;
+       struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
 
-       ret = pci_enable_device(pdev);
-       if (ret) {
-               dev_err(&pdev->dev, "can't enable device\n");
-               goto err;
-       }
+       return ath_reg_notifier_apply(wiphy, request, regulatory);
+}
 
-       /* XXX 32-bit addressing only */
-       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
-       if (ret) {
-               dev_err(&pdev->dev, "32-bit DMA not available\n");
-               goto err_dis;
-       }
+/********************\
+* Channel/mode setup *
+\********************/
 
-       /*
-        * 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 >> 2;
-               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);
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static inline short
+ath5k_ieee2mhz(short chan)
+{
+       if (chan <= 14 || chan >= 27)
+               return ieee80211chan2mhz(chan);
+       else
+               return 2212 + chan * 20;
+}
 
-       /* Enable bus mastering */
-       pci_set_master(pdev);
+/*
+ * Returns true for the channel numbers used without all_channels modparam.
+ */
+static bool ath5k_is_standard_channel(short chan)
+{
+       return ((chan <= 14) ||
+               /* UNII 1,2 */
+               ((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
+               /* midband */
+               ((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
+               /* UNII-3 */
+               ((chan & 3) == 1 && chan >= 149 && chan <= 165));
+}
 
-       /*
-        * Disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state.
-        */
-       pci_write_config_byte(pdev, 0x41, 0);
+static unsigned int
+ath5k_copy_channels(struct ath5k_hw *ah,
+               struct ieee80211_channel *channels,
+               unsigned int mode,
+               unsigned int max)
+{
+       unsigned int i, count, size, chfreq, freq, ch;
 
-       ret = pci_request_region(pdev, 0, "ath5k");
-       if (ret) {
-               dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
-               goto err_dis;
-       }
+       if (!test_bit(mode, ah->ah_modes))
+               return 0;
 
-       mem = pci_iomap(pdev, 0, 0);
-       if (!mem) {
-               dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
-               ret = -EIO;
-               goto err_reg;
+       switch (mode) {
+       case AR5K_MODE_11A:
+       case AR5K_MODE_11A_TURBO:
+               /* 1..220, but 2GHz frequencies are filtered by check_channel */
+               size = 220 ;
+               chfreq = CHANNEL_5GHZ;
+               break;
+       case AR5K_MODE_11B:
+       case AR5K_MODE_11G:
+       case AR5K_MODE_11G_TURBO:
+               size = 26;
+               chfreq = CHANNEL_2GHZ;
+               break;
+       default:
+               ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
+               return 0;
        }
 
-       /*
-        * Allocate hw (mac80211 main struct)
-        * and hw->priv (driver private data)
-        */
-       hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
-       if (hw == NULL) {
-               dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
-               ret = -ENOMEM;
-               goto err_map;
-       }
+       for (i = 0, count = 0; i < size && max > 0; i++) {
+               ch = i + 1 ;
+               freq = ath5k_ieee2mhz(ch);
 
-       dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
-
-       /* Initialize driver private data */
-       SET_IEEE80211_DEV(hw, &pdev->dev);
-       hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
-                   IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-                   IEEE80211_HW_SIGNAL_DBM;
+               /* Check if channel is supported by the chipset */
+               if (!ath5k_channel_ok(ah, freq, chfreq))
+                       continue;
 
-       hw->wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_AP) |
-               BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC) |
-               BIT(NL80211_IFTYPE_MESH_POINT);
+               if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
+                       continue;
 
-       hw->extra_tx_headroom = 2;
-       hw->channel_change_time = 5000;
-       sc = hw->priv;
-       sc->hw = hw;
-       sc->pdev = pdev;
+               /* Write channel info and increment counter */
+               channels[count].center_freq = freq;
+               channels[count].band = (chfreq == CHANNEL_2GHZ) ?
+                       IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+               switch (mode) {
+               case AR5K_MODE_11A:
+               case AR5K_MODE_11G:
+                       channels[count].hw_value = chfreq | CHANNEL_OFDM;
+                       break;
+               case AR5K_MODE_11A_TURBO:
+               case AR5K_MODE_11G_TURBO:
+                       channels[count].hw_value = chfreq |
+                               CHANNEL_OFDM | CHANNEL_TURBO;
+                       break;
+               case AR5K_MODE_11B:
+                       channels[count].hw_value = CHANNEL_B;
+               }
 
-       ath5k_debug_init_device(sc);
+               count++;
+               max--;
+       }
 
-       /*
-        * Mark the device as detached to avoid processing
-        * interrupts until setup is complete.
-        */
-       __set_bit(ATH_STAT_INVALID, sc->status);
+       return count;
+}
 
-       sc->iobase = mem; /* So we can unmap it on detach */
-       sc->opmode = NL80211_IFTYPE_STATION;
-       sc->bintval = 1000;
-       mutex_init(&sc->lock);
-       spin_lock_init(&sc->rxbuflock);
-       spin_lock_init(&sc->txbuflock);
-       spin_lock_init(&sc->block);
+static void
+ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b)
+{
+       u8 i;
 
-       /* Set private data */
-       pci_set_drvdata(pdev, sc);
+       for (i = 0; i < AR5K_MAX_RATES; i++)
+               sc->rate_idx[b->band][i] = -1;
 
-       /* Setup interrupt handler */
-       ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
-       if (ret) {
-               ATH5K_ERR(sc, "request_irq failed\n");
-               goto err_free;
+       for (i = 0; i < b->n_bitrates; i++) {
+               sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
+               if (b->bitrates[i].hw_value_short)
+                       sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
        }
+}
 
-       /* If we passed the test, malloc an ath5k_hw struct */
-       sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
-       if (!sc->ah) {
-               ret = -ENOMEM;
-               ATH5K_ERR(sc, "out of memory\n");
-               goto err_irq;
-       }
+static int
+ath5k_setup_bands(struct ieee80211_hw *hw)
+{
+       struct ath5k_softc *sc = hw->priv;
+       struct ath5k_hw *ah = sc->ah;
+       struct ieee80211_supported_band *sband;
+       int max_c, count_c = 0;
+       int i;
 
-       sc->ah->ah_sc = sc;
-       sc->ah->ah_iobase = sc->iobase;
-       common = ath5k_hw_common(sc->ah);
-       common->ops = &ath5k_common_ops;
-       common->ah = sc->ah;
-       common->hw = hw;
-       common->cachelsz = csz << 2; /* convert to bytes */
+       BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
+       max_c = ARRAY_SIZE(sc->channels);
 
-       /* Initialize device */
-       ret = ath5k_hw_attach(sc);
-       if (ret) {
-               goto err_free_ah;
-       }
+       /* 2GHz band */
+       sband = &sc->sbands[IEEE80211_BAND_2GHZ];
+       sband->band = IEEE80211_BAND_2GHZ;
+       sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
 
-       /* set up multi-rate retry capabilities */
-       if (sc->ah->ah_version == AR5K_AR5212) {
-               hw->max_rates = 4;
-               hw->max_rate_tries = 11;
-       }
+       if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
+               /* G mode */
+               memcpy(sband->bitrates, &ath5k_rates[0],
+                      sizeof(struct ieee80211_rate) * 12);
+               sband->n_bitrates = 12;
 
-       /* Finish private driver data initialization */
-       ret = ath5k_attach(pdev, hw);
-       if (ret)
-               goto err_ah;
+               sband->channels = sc->channels;
+               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+                                       AR5K_MODE_11G, max_c);
 
-       ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
-                       ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
-                                       sc->ah->ah_mac_srev,
-                                       sc->ah->ah_phy_revision);
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+               count_c = sband->n_channels;
+               max_c -= count_c;
+       } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
+               /* B mode */
+               memcpy(sband->bitrates, &ath5k_rates[0],
+                      sizeof(struct ieee80211_rate) * 4);
+               sband->n_bitrates = 4;
 
-       if (!sc->ah->ah_single_chip) {
-               /* Single chip radio (!RF5111) */
-               if (sc->ah->ah_radio_5ghz_revision &&
-                       !sc->ah->ah_radio_2ghz_revision) {
-                       /* No 5GHz support -> report 2GHz radio */
-                       if (!test_bit(AR5K_MODE_11A,
-                               sc->ah->ah_capabilities.cap_mode)) {
-                               ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
-                                       ath5k_chip_name(AR5K_VERSION_RAD,
-                                               sc->ah->ah_radio_5ghz_revision),
-                                               sc->ah->ah_radio_5ghz_revision);
-                       /* No 2GHz support (5110 and some
-                        * 5Ghz only cards) -> report 5Ghz radio */
-                       } else if (!test_bit(AR5K_MODE_11B,
-                               sc->ah->ah_capabilities.cap_mode)) {
-                               ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
-                                       ath5k_chip_name(AR5K_VERSION_RAD,
-                                               sc->ah->ah_radio_5ghz_revision),
-                                               sc->ah->ah_radio_5ghz_revision);
-                       /* Multiband radio */
-                       } else {
-                               ATH5K_INFO(sc, "RF%s multiband radio found"
-                                       " (0x%x)\n",
-                                       ath5k_chip_name(AR5K_VERSION_RAD,
-                                               sc->ah->ah_radio_5ghz_revision),
-                                               sc->ah->ah_radio_5ghz_revision);
+               /* 5211 only supports B rates and uses 4bit rate codes
+                * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
+                * fix them up here:
+                */
+               if (ah->ah_version == AR5K_AR5211) {
+                       for (i = 0; i < 4; i++) {
+                               sband->bitrates[i].hw_value =
+                                       sband->bitrates[i].hw_value & 0xF;
+                               sband->bitrates[i].hw_value_short =
+                                       sband->bitrates[i].hw_value_short & 0xF;
                        }
                }
-               /* Multi chip radio (RF5111 - RF2111) ->
-                * report both 2GHz/5GHz radios */
-               else if (sc->ah->ah_radio_5ghz_revision &&
-                               sc->ah->ah_radio_2ghz_revision){
-                       ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
-                               ath5k_chip_name(AR5K_VERSION_RAD,
-                                       sc->ah->ah_radio_5ghz_revision),
-                                       sc->ah->ah_radio_5ghz_revision);
-                       ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
-                               ath5k_chip_name(AR5K_VERSION_RAD,
-                                       sc->ah->ah_radio_2ghz_revision),
-                                       sc->ah->ah_radio_2ghz_revision);
-               }
-       }
 
+               sband->channels = sc->channels;
+               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+                                       AR5K_MODE_11B, max_c);
 
-       /* ready to process interrupts */
-       __clear_bit(ATH_STAT_INVALID, sc->status);
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+               count_c = sband->n_channels;
+               max_c -= count_c;
+       }
+       ath5k_setup_rate_idx(sc, sband);
 
-       return 0;
-err_ah:
-       ath5k_hw_detach(sc->ah);
-err_free_ah:
-       kfree(sc->ah);
-err_irq:
-       free_irq(pdev->irq, sc);
-err_free:
-       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;
-}
+       /* 5GHz band, A mode */
+       if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
+               sband = &sc->sbands[IEEE80211_BAND_5GHZ];
+               sband->band = IEEE80211_BAND_5GHZ;
+               sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
 
-static void __devexit
-ath5k_pci_remove(struct pci_dev *pdev)
-{
-       struct ath5k_softc *sc = pci_get_drvdata(pdev);
+               memcpy(sband->bitrates, &ath5k_rates[4],
+                      sizeof(struct ieee80211_rate) * 8);
+               sband->n_bitrates = 8;
 
-       ath5k_debug_finish_device(sc);
-       ath5k_detach(pdev, sc->hw);
-       ath5k_hw_detach(sc->ah);
-       kfree(sc->ah);
-       free_irq(pdev->irq, sc);
-       pci_iounmap(pdev, sc->iobase);
-       pci_release_region(pdev, 0);
-       pci_disable_device(pdev);
-       ieee80211_free_hw(sc->hw);
-}
+               sband->channels = &sc->channels[count_c];
+               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+                                       AR5K_MODE_11A, max_c);
 
-#ifdef CONFIG_PM_SLEEP
-static int ath5k_pci_suspend(struct device *dev)
-{
-       struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
+       }
+       ath5k_setup_rate_idx(sc, sband);
+
+       ath5k_debug_dump_bands(sc);
 
-       ath5k_led_off(sc);
        return 0;
 }
 
-static int ath5k_pci_resume(struct device *dev)
-{
-       struct pci_dev *pdev = to_pci_dev(dev);
-       struct ath5k_softc *sc = pci_get_drvdata(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);
+/*
+ * Set/change channels. We always reset the chip.
+ * To accomplish this we must first cleanup any pending DMA,
+ * then restart stuff after a la  ath5k_init.
+ *
+ * Called with sc->lock.
+ */
+static int
+ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+{
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                 "channel set, resetting (%u -> %u MHz)\n",
+                 sc->curchan->center_freq, chan->center_freq);
 
-       ath5k_led_enable(sc);
-       return 0;
+       /*
+        * 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.
+        */
+       return ath5k_reset(sc, chan);
 }
-#endif /* CONFIG_PM_SLEEP */
 
+static void
+ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
+{
+       sc->curmode = mode;
 
-/***********************\
-* Driver Initialization *
-\***********************/
+       if (mode == AR5K_MODE_11A) {
+               sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
+       } else {
+               sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
+       }
+}
 
-static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+static void
+ath5k_mode_setup(struct ath5k_softc *sc)
 {
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-       struct ath5k_softc *sc = hw->priv;
-       struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
+       struct ath5k_hw *ah = sc->ah;
+       u32 rfilt;
 
-       return ath_reg_notifier_apply(wiphy, request, regulatory);
+       /* 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, sc->opmode);
+
+       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
+       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
 }
 
-static int
-ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+static inline int
+ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
 {
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
-       u8 mac[ETH_ALEN] = {};
-       int ret;
+       int rix;
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
+       /* return base rate on errors */
+       if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
+                       "hw_rix out of bounds: %x\n", hw_rix))
+               return 0;
 
-       /*
-        * Check if the MAC has multi-rate retry support.
-        * We do this by trying to setup a fake extended
-        * descriptor.  MACs that don't have support will
-        * return false w/o doing anything.  MACs that do
-        * support it will return true w/o doing anything.
-        */
-       ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
+       rix = sc->rate_idx[sc->curband->band][hw_rix];
+       if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
+               rix = 0;
 
-       if (ret < 0)
-               goto err;
-       if (ret > 0)
-               __set_bit(ATH_STAT_MRRETRY, sc->status);
+       return rix;
+}
 
-       /*
-        * Collect the channel list.  The 802.11 layer
-        * is resposible for filtering this list based
-        * on settings like the phy mode and regulatory
-        * domain restrictions.
-        */
-       ret = ath5k_setup_bands(hw);
-       if (ret) {
-               ATH5K_ERR(sc, "can't get channels\n");
-               goto err;
-       }
+/***************\
+* Buffers setup *
+\***************/
 
-       /* NB: setup here so ath5k_rate_update is happy */
-       if (test_bit(AR5K_MODE_11A, ah->ah_modes))
-               ath5k_setcurmode(sc, AR5K_MODE_11A);
-       else
-               ath5k_setcurmode(sc, AR5K_MODE_11B);
+static
+struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
+{
+       struct ath_common *common = ath5k_hw_common(sc->ah);
+       struct sk_buff *skb;
 
        /*
-        * Allocate tx+rx descriptors and populate the lists.
+        * Allocate buffer with headroom_needed space for the
+        * fake physical layer header at the start.
         */
-       ret = ath5k_desc_alloc(sc, pdev);
-       if (ret) {
-               ATH5K_ERR(sc, "can't allocate descriptors\n");
-               goto err;
-       }
+       skb = ath_rxbuf_alloc(common,
+                             common->rx_bufsize,
+                             GFP_ATOMIC);
 
-       /*
-        * Allocate hardware transmit queues: one queue for
-        * beacon frames and one data queue for each QoS
-        * priority.  Note that hw functions handle resetting
-        * these queues at the needed time.
-        */
-       ret = ath5k_beaconq_setup(ah);
-       if (ret < 0) {
-               ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
-               goto err_desc;
-       }
-       sc->bhalq = ret;
-       sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
-       if (IS_ERR(sc->cabq)) {
-               ATH5K_ERR(sc, "can't setup cab queue\n");
-               ret = PTR_ERR(sc->cabq);
-               goto err_bhal;
+       if (!skb) {
+               ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
+                               common->rx_bufsize);
+               return NULL;
        }
 
-       sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
-       if (IS_ERR(sc->txq)) {
-               ATH5K_ERR(sc, "can't setup xmit queue\n");
-               ret = PTR_ERR(sc->txq);
-               goto err_queues;
+       *skb_addr = pci_map_single(sc->pdev,
+                                  skb->data, common->rx_bufsize,
+                                  PCI_DMA_FROMDEVICE);
+       if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
+               ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
+               dev_kfree_skb(skb);
+               return NULL;
        }
+       return skb;
+}
 
-       tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
-       tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
-       tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
-       tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
-       tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
-
-       INIT_WORK(&sc->reset_work, ath5k_reset_work);
+static int
+ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+       struct ath5k_hw *ah = sc->ah;
+       struct sk_buff *skb = bf->skb;
+       struct ath5k_desc *ds;
+       int ret;
 
-       ret = ath5k_eeprom_read_mac(ah, mac);
-       if (ret) {
-               ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
-                       sc->pdev->device);
-               goto err_queues;
+       if (!skb) {
+               skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
+               if (!skb)
+                       return -ENOMEM;
+               bf->skb = skb;
        }
 
-       SET_IEEE80211_PERM_ADDR(hw, mac);
-       /* All MAC address bits matter for ACKs */
-       memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
-       ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
-
-       regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
-       ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
+       /*
+        * 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 ensure 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 ensures 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;
+       ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
        if (ret) {
-               ATH5K_ERR(sc, "can't initialize regulatory system\n");
-               goto err_queues;
+               ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
+               return ret;
        }
 
-       ret = ieee80211_register_hw(hw);
-       if (ret) {
-               ATH5K_ERR(sc, "can't register ieee80211 hw\n");
-               goto err_queues;
-       }
+       if (sc->rxlink != NULL)
+               *sc->rxlink = bf->daddr;
+       sc->rxlink = &ds->ds_link;
+       return 0;
+}
 
-       if (!ath_is_world_regd(regulatory))
-               regulatory_hint(hw->wiphy, regulatory->alpha2);
+static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       enum ath5k_pkt_type htype;
+       __le16 fc;
 
-       ath5k_init_leds(sc);
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = hdr->frame_control;
 
-       ath5k_sysfs_register(sc);
+       if (ieee80211_is_beacon(fc))
+               htype = AR5K_PKT_TYPE_BEACON;
+       else if (ieee80211_is_probe_resp(fc))
+               htype = AR5K_PKT_TYPE_PROBE_RESP;
+       else if (ieee80211_is_atim(fc))
+               htype = AR5K_PKT_TYPE_ATIM;
+       else if (ieee80211_is_pspoll(fc))
+               htype = AR5K_PKT_TYPE_PSPOLL;
+       else
+               htype = AR5K_PKT_TYPE_NORMAL;
 
-       return 0;
-err_queues:
-       ath5k_txq_release(sc);
-err_bhal:
-       ath5k_hw_release_tx_queue(ah, sc->bhalq);
-err_desc:
-       ath5k_desc_free(sc, pdev);
-err:
-       return ret;
+       return htype;
 }
 
-static void
-ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+static int
+ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
+                 struct ath5k_txq *txq, int padsize)
 {
-       struct ath5k_softc *sc = hw->priv;
-
-       /*
-        * NB: the order of these is important:
-        * o call the 802.11 layer before detaching ath5k_hw to
-        *   ensure 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
-        * XXX: ??? detach ath5k_hw ???
-        * Other than that, it's straightforward...
-        */
-       ieee80211_unregister_hw(hw);
-       ath5k_desc_free(sc, pdev);
-       ath5k_txq_release(sc);
-       ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
-       ath5k_unregister_leds(sc);
-
-       ath5k_sysfs_unregister(sc);
-       /*
-        * 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.
-        */
-}
+       struct ath5k_hw *ah = sc->ah;
+       struct ath5k_desc *ds = bf->desc;
+       struct sk_buff *skb = bf->skb;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
+       struct ieee80211_rate *rate;
+       unsigned int mrr_rate[3], mrr_tries[3];
+       int i, ret;
+       u16 hw_rate;
+       u16 cts_rate = 0;
+       u16 duration = 0;
+       u8 rc_flags;
 
+       flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
 
+       /* XXX endianness */
+       bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+                       PCI_DMA_TODEVICE);
 
+       rate = ieee80211_get_tx_rate(sc->hw, info);
+       if (!rate) {
+               ret = -EINVAL;
+               goto err_unmap;
+       }
 
-/********************\
-* Channel/mode setup *
-\********************/
+       if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+               flags |= AR5K_TXDESC_NOACK;
 
-/*
- * Convert IEEE channel number to MHz frequency.
- */
-static inline short
-ath5k_ieee2mhz(short chan)
-{
-       if (chan <= 14 || chan >= 27)
-               return ieee80211chan2mhz(chan);
-       else
-               return 2212 + chan * 20;
-}
+       rc_flags = info->control.rates[0].flags;
+       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+               rate->hw_value_short : rate->hw_value;
 
-/*
- * Returns true for the channel numbers used without all_channels modparam.
- */
-static bool ath5k_is_standard_channel(short chan)
-{
-       return ((chan <= 14) ||
-               /* UNII 1,2 */
-               ((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
-               /* midband */
-               ((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
-               /* UNII-3 */
-               ((chan & 3) == 1 && chan >= 149 && chan <= 165));
-}
+       pktlen = skb->len;
 
-static unsigned int
-ath5k_copy_channels(struct ath5k_hw *ah,
-               struct ieee80211_channel *channels,
-               unsigned int mode,
-               unsigned int max)
-{
-       unsigned int i, count, size, chfreq, freq, ch;
+       /* FIXME: If we are in g mode and rate is a CCK rate
+        * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+        * from tx power (value is in dB units already) */
+       if (info->control.hw_key) {
+               keyidx = info->control.hw_key->hw_key_idx;
+               pktlen += info->control.hw_key->icv_len;
+       }
+       if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+               flags |= AR5K_TXDESC_RTSENA;
+               cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
+               duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
+                       sc->vif, pktlen, info));
+       }
+       if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+               flags |= AR5K_TXDESC_CTSENA;
+               cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
+               duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
+                       sc->vif, pktlen, info));
+       }
+       ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+               ieee80211_get_hdrlen_from_skb(skb), padsize,
+               get_hw_packet_type(skb),
+               (sc->power_level * 2),
+               hw_rate,
+               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+               cts_rate, duration);
+       if (ret)
+               goto err_unmap;
 
-       if (!test_bit(mode, ah->ah_modes))
-               return 0;
+       memset(mrr_rate, 0, sizeof(mrr_rate));
+       memset(mrr_tries, 0, sizeof(mrr_tries));
+       for (i = 0; i < 3; i++) {
+               rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
+               if (!rate)
+                       break;
 
-       switch (mode) {
-       case AR5K_MODE_11A:
-       case AR5K_MODE_11A_TURBO:
-               /* 1..220, but 2GHz frequencies are filtered by check_channel */
-               size = 220 ;
-               chfreq = CHANNEL_5GHZ;
-               break;
-       case AR5K_MODE_11B:
-       case AR5K_MODE_11G:
-       case AR5K_MODE_11G_TURBO:
-               size = 26;
-               chfreq = CHANNEL_2GHZ;
-               break;
-       default:
-               ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
-               return 0;
+               mrr_rate[i] = rate->hw_value;
+               mrr_tries[i] = info->control.rates[i + 1].count;
        }
 
-       for (i = 0, count = 0; i < size && max > 0; i++) {
-               ch = i + 1 ;
-               freq = ath5k_ieee2mhz(ch);
-
-               /* Check if channel is supported by the chipset */
-               if (!ath5k_channel_ok(ah, freq, chfreq))
-                       continue;
+       ath5k_hw_setup_mrr_tx_desc(ah, ds,
+               mrr_rate[0], mrr_tries[0],
+               mrr_rate[1], mrr_tries[1],
+               mrr_rate[2], mrr_tries[2]);
 
-               if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
-                       continue;
+       ds->ds_link = 0;
+       ds->ds_data = bf->skbaddr;
 
-               /* Write channel info and increment counter */
-               channels[count].center_freq = freq;
-               channels[count].band = (chfreq == CHANNEL_2GHZ) ?
-                       IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-               switch (mode) {
-               case AR5K_MODE_11A:
-               case AR5K_MODE_11G:
-                       channels[count].hw_value = chfreq | CHANNEL_OFDM;
-                       break;
-               case AR5K_MODE_11A_TURBO:
-               case AR5K_MODE_11G_TURBO:
-                       channels[count].hw_value = chfreq |
-                               CHANNEL_OFDM | CHANNEL_TURBO;
-                       break;
-               case AR5K_MODE_11B:
-                       channels[count].hw_value = CHANNEL_B;
-               }
+       spin_lock_bh(&txq->lock);
+       list_add_tail(&bf->list, &txq->q);
+       txq->txq_len++;
+       if (txq->link == NULL) /* is this first packet? */
+               ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
+       else /* no, so only link it */
+               *txq->link = bf->daddr;
 
-               count++;
-               max--;
-       }
+       txq->link = &ds->ds_link;
+       ath5k_hw_start_tx_dma(ah, txq->qnum);
+       mmiowb();
+       spin_unlock_bh(&txq->lock);
 
-       return count;
+       return 0;
+err_unmap:
+       pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+       return ret;
 }
 
-static void
-ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b)
+/*******************\
+* Descriptors setup *
+\*******************/
+
+static int
+ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
 {
-       u8 i;
+       struct ath5k_desc *ds;
+       struct ath5k_buf *bf;
+       dma_addr_t da;
+       unsigned int i;
+       int ret;
 
-       for (i = 0; i < AR5K_MAX_RATES; i++)
-               sc->rate_idx[b->band][i] = -1;
+       /* allocate descriptors */
+       sc->desc_len = sizeof(struct ath5k_desc) *
+                       (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
+       sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
+       if (sc->desc == NULL) {
+               ATH5K_ERR(sc, "can't allocate descriptors\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       ds = sc->desc;
+       da = sc->desc_daddr;
+       ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
+               ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
 
-       for (i = 0; i < b->n_bitrates; i++) {
-               sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
-               if (b->bitrates[i].hw_value_short)
-                       sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
+       bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
+                       sizeof(struct ath5k_buf), GFP_KERNEL);
+       if (bf == NULL) {
+               ATH5K_ERR(sc, "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++,
+                       da += 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 int
-ath5k_setup_bands(struct ieee80211_hw *hw)
+static void
+ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
 {
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       struct ieee80211_supported_band *sband;
-       int max_c, count_c = 0;
-       int i;
+       struct ath5k_buf *bf;
 
-       BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
-       max_c = ARRAY_SIZE(sc->channels);
+       ath5k_txbuf_free_skb(sc, sc->bbuf);
+       list_for_each_entry(bf, &sc->txbuf, list)
+               ath5k_txbuf_free_skb(sc, bf);
+       list_for_each_entry(bf, &sc->rxbuf, list)
+               ath5k_rxbuf_free_skb(sc, bf);
 
-       /* 2GHz band */
-       sband = &sc->sbands[IEEE80211_BAND_2GHZ];
-       sband->band = IEEE80211_BAND_2GHZ;
-       sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
+       /* Free memory associated with all descriptors */
+       pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+       sc->desc = NULL;
+       sc->desc_daddr = 0;
 
-       if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
-               /* G mode */
-               memcpy(sband->bitrates, &ath5k_rates[0],
-                      sizeof(struct ieee80211_rate) * 12);
-               sband->n_bitrates = 12;
+       kfree(sc->bufptr);
+       sc->bufptr = NULL;
+       sc->bbuf = NULL;
+}
 
-               sband->channels = sc->channels;
-               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-                                       AR5K_MODE_11G, max_c);
 
-               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
-               count_c = sband->n_channels;
-               max_c -= count_c;
-       } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
-               /* B mode */
-               memcpy(sband->bitrates, &ath5k_rates[0],
-                      sizeof(struct ieee80211_rate) * 4);
-               sband->n_bitrates = 4;
+/**************\
+* Queues setup *
+\**************/
 
-               /* 5211 only supports B rates and uses 4bit rate codes
-                * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
-                * fix them up here:
+static struct ath5k_txq *
+ath5k_txq_setup(struct ath5k_softc *sc,
+               int qtype, int subtype)
+{
+       struct ath5k_hw *ah = sc->ah;
+       struct ath5k_txq *txq;
+       struct ath5k_txq_info qi = {
+               .tqi_subtype = subtype,
+               /* XXX: default values not correct for B and XR channels,
+                * but who cares? */
+               .tqi_aifs = AR5K_TUNE_AIFS,
+               .tqi_cw_min = AR5K_TUNE_CWMIN,
+               .tqi_cw_max = AR5K_TUNE_CWMAX
+       };
+       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 we wait 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
                 */
-               if (ah->ah_version == AR5K_AR5211) {
-                       for (i = 0; i < 4; i++) {
-                               sband->bitrates[i].hw_value =
-                                       sband->bitrates[i].hw_value & 0xF;
-                               sband->bitrates[i].hw_value_short =
-                                       sband->bitrates[i].hw_value_short & 0xF;
-                       }
-               }
+               return ERR_PTR(qnum);
+       }
+       if (qnum >= ARRAY_SIZE(sc->txqs)) {
+               ATH5K_ERR(sc, "hw 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;
+               txq->txq_len = 0;
+               txq->txq_poll_mark = false;
+               txq->txq_stuck = 0;
+       }
+       return &sc->txqs[qnum];
+}
 
-               sband->channels = sc->channels;
-               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-                                       AR5K_MODE_11B, max_c);
+static int
+ath5k_beaconq_setup(struct ath5k_hw *ah)
+{
+       struct ath5k_txq_info qi = {
+               /* XXX: default values not correct for B and XR channels,
+                * but who cares? */
+               .tqi_aifs = AR5K_TUNE_AIFS,
+               .tqi_cw_min = AR5K_TUNE_CWMIN,
+               .tqi_cw_max = AR5K_TUNE_CWMAX,
+               /* 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);
+}
 
-               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
-               count_c = sband->n_channels;
-               max_c -= count_c;
-       }
-       ath5k_setup_rate_idx(sc, sband);
+static int
+ath5k_beaconq_config(struct ath5k_softc *sc)
+{
+       struct ath5k_hw *ah = sc->ah;
+       struct ath5k_txq_info qi;
+       int ret;
 
-       /* 5GHz band, A mode */
-       if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
-               sband = &sc->sbands[IEEE80211_BAND_5GHZ];
-               sband->band = IEEE80211_BAND_5GHZ;
-               sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
+       ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
+       if (ret)
+               goto err;
 
-               memcpy(sband->bitrates, &ath5k_rates[4],
-                      sizeof(struct ieee80211_rate) * 8);
-               sband->n_bitrates = 8;
+       if (sc->opmode == NL80211_IFTYPE_AP ||
+               sc->opmode == NL80211_IFTYPE_MESH_POINT) {
+               /*
+                * Always burst out beacon and CAB traffic
+                * (aifs = cwmin = cwmax = 0)
+                */
+               qi.tqi_aifs = 0;
+               qi.tqi_cw_min = 0;
+               qi.tqi_cw_max = 0;
+       } else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+               /*
+                * Adhoc mode; backoff between 0 and (2 * cw_min).
+                */
+               qi.tqi_aifs = 0;
+               qi.tqi_cw_min = 0;
+               qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;
+       }
 
-               sband->channels = &sc->channels[count_c];
-               sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-                                       AR5K_MODE_11A, max_c);
+       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+               "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
+               qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
 
-               hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
+       ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
+       if (ret) {
+               ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
+                       "hardware queue!\n", __func__);
+               goto err;
        }
-       ath5k_setup_rate_idx(sc, sband);
+       ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
+       if (ret)
+               goto err;
 
-       ath5k_debug_dump_bands(sc);
+       /* reconfigure cabq with ready time to 80% of beacon_interval */
+       ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+       if (ret)
+               goto err;
 
-       return 0;
+       qi.tqi_ready_time = (sc->bintval * 80) / 100;
+       ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+       if (ret)
+               goto err;
+
+       ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
+err:
+       return ret;
 }
 
-/*
- * Set/change channels. We always reset the chip.
- * To accomplish this we must first cleanup any pending DMA,
- * then restart stuff after a la  ath5k_init.
- *
- * Called with sc->lock.
- */
-static int
-ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+static void
+ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 {
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                 "channel set, resetting (%u -> %u MHz)\n",
-                 sc->curchan->center_freq, chan->center_freq);
+       struct ath5k_buf *bf, *bf0;
 
        /*
-        * 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.
+        * NB: this assumes output has been stopped and
+        *     we do not need to block ath5k_tx_tasklet
         */
-       return ath5k_reset(sc, chan);
-}
+       spin_lock_bh(&txq->lock);
+       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+               ath5k_debug_printtxbuf(sc, bf);
 
-static void
-ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
-{
-       sc->curmode = mode;
+               ath5k_txbuf_free_skb(sc, bf);
 
-       if (mode == AR5K_MODE_11A) {
-               sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
-       } else {
-               sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
+               spin_lock_bh(&sc->txbuflock);
+               list_move_tail(&bf->list, &sc->txbuf);
+               sc->txbuf_len++;
+               txq->txq_len--;
+               spin_unlock_bh(&sc->txbuflock);
        }
+       txq->link = NULL;
+       txq->txq_poll_mark = false;
+       spin_unlock_bh(&txq->lock);
 }
 
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
 static void
-ath5k_mode_setup(struct ath5k_softc *sc)
+ath5k_txq_cleanup(struct ath5k_softc *sc)
 {
        struct ath5k_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);
+       unsigned int i;
 
-       /* configure operational mode */
-       ath5k_hw_set_opmode(ah, sc->opmode);
+       /* XXX return value */
+       if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
+               /* don't touch the hardware if marked invalid */
+               ath5k_hw_stop_tx_dma(ah, sc->bhalq);
+               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
+                       ath5k_hw_get_txdp(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);
+                               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
+                                       "link %p\n",
+                                       sc->txqs[i].qnum,
+                                       ath5k_hw_get_txdp(ah,
+                                                       sc->txqs[i].qnum),
+                                       sc->txqs[i].link);
+                       }
+       }
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
-       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
+       for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+               if (sc->txqs[i].setup)
+                       ath5k_txq_drainq(sc, &sc->txqs[i]);
 }
 
-static inline int
-ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
+static void
+ath5k_txq_release(struct ath5k_softc *sc)
 {
-       int rix;
-
-       /* return base rate on errors */
-       if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
-                       "hw_rix out of bounds: %x\n", hw_rix))
-               return 0;
-
-       rix = sc->rate_idx[sc->curband->band][hw_rix];
-       if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
-               rix = 0;
+       struct ath5k_txq *txq = sc->txqs;
+       unsigned int i;
 
-       return rix;
+       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;
+               }
 }
 
-/***************\
-* Buffers setup *
-\***************/
-
-static
-struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
-{
-       struct ath_common *common = ath5k_hw_common(sc->ah);
-       struct sk_buff *skb;
-
-       /*
-        * Allocate buffer with headroom_needed space for the
-        * fake physical layer header at the start.
-        */
-       skb = ath_rxbuf_alloc(common,
-                             common->rx_bufsize,
-                             GFP_ATOMIC);
-
-       if (!skb) {
-               ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
-                               common->rx_bufsize);
-               return NULL;
-       }
 
-       *skb_addr = pci_map_single(sc->pdev,
-                                  skb->data, common->rx_bufsize,
-                                  PCI_DMA_FROMDEVICE);
-       if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
-               ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
-               dev_kfree_skb(skb);
-               return NULL;
-       }
-       return skb;
-}
+/*************\
+* RX Handling *
+\*************/
 
+/*
+ * Enable the receive h/w following a reset.
+ */
 static int
-ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_rx_start(struct ath5k_softc *sc)
 {
        struct ath5k_hw *ah = sc->ah;
-       struct sk_buff *skb = bf->skb;
-       struct ath5k_desc *ds;
+       struct ath_common *common = ath5k_hw_common(ah);
+       struct ath5k_buf *bf;
        int ret;
 
-       if (!skb) {
-               skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
-               if (!skb)
-                       return -ENOMEM;
-               bf->skb = skb;
-       }
+       common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
 
-       /*
-        * 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 ensure 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 ensures 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;
-       ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
-       if (ret) {
-               ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
-               return ret;
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+                 common->cachelsz, common->rx_bufsize);
+
+       spin_lock_bh(&sc->rxbuflock);
+       sc->rxlink = NULL;
+       list_for_each_entry(bf, &sc->rxbuf, list) {
+               ret = ath5k_rxbuf_setup(sc, bf);
+               if (ret != 0) {
+                       spin_unlock_bh(&sc->rxbuflock);
+                       goto err;
+               }
        }
+       bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+       ath5k_hw_set_rxdp(ah, bf->daddr);
+       spin_unlock_bh(&sc->rxbuflock);
+
+       ath5k_hw_start_rx_dma(ah);      /* enable recv descriptors */
+       ath5k_mode_setup(sc);           /* set filters, etc. */
+       ath5k_hw_start_rx_pcu(ah);      /* re-enable PCU/DMA engine */
 
-       if (sc->rxlink != NULL)
-               *sc->rxlink = bf->daddr;
-       sc->rxlink = &ds->ds_link;
        return 0;
+err:
+       return ret;
 }
 
-static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath5k_rx_stop(struct ath5k_softc *sc)
 {
-       struct ieee80211_hdr *hdr;
-       enum ath5k_pkt_type htype;
-       __le16 fc;
-
-       hdr = (struct ieee80211_hdr *)skb->data;
-       fc = hdr->frame_control;
+       struct ath5k_hw *ah = sc->ah;
 
-       if (ieee80211_is_beacon(fc))
-               htype = AR5K_PKT_TYPE_BEACON;
-       else if (ieee80211_is_probe_resp(fc))
-               htype = AR5K_PKT_TYPE_PROBE_RESP;
-       else if (ieee80211_is_atim(fc))
-               htype = AR5K_PKT_TYPE_ATIM;
-       else if (ieee80211_is_pspoll(fc))
-               htype = AR5K_PKT_TYPE_PSPOLL;
-       else
-               htype = AR5K_PKT_TYPE_NORMAL;
+       ath5k_hw_stop_rx_pcu(ah);       /* disable PCU */
+       ath5k_hw_set_rx_filter(ah, 0);  /* clear recv filter */
+       ath5k_hw_stop_rx_dma(ah);       /* disable DMA engine */
 
-       return htype;
+       ath5k_debug_printrxbuffs(sc, ah);
 }
 
-static int
-ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
-                 struct ath5k_txq *txq, int padsize)
+static unsigned int
+ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
+                  struct ath5k_rx_status *rs)
 {
        struct ath5k_hw *ah = sc->ah;
-       struct ath5k_desc *ds = bf->desc;
-       struct sk_buff *skb = bf->skb;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
-       struct ieee80211_rate *rate;
-       unsigned int mrr_rate[3], mrr_tries[3];
-       int i, ret;
-       u16 hw_rate;
-       u16 cts_rate = 0;
-       u16 duration = 0;
-       u8 rc_flags;
+       struct ath_common *common = ath5k_hw_common(ah);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       unsigned int keyix, hlen;
 
-       flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
+       if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+                       rs->rs_keyix != AR5K_RXKEYIX_INVALID)
+               return RX_FLAG_DECRYPTED;
 
-       /* XXX endianness */
-       bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
-                       PCI_DMA_TODEVICE);
+       /* Apparently when a default key is used to decrypt the packet
+          the hw does not set the index used to decrypt.  In such cases
+          get the index from the packet. */
+       hlen = ieee80211_hdrlen(hdr->frame_control);
+       if (ieee80211_has_protected(hdr->frame_control) &&
+           !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+           skb->len >= hlen + 4) {
+               keyix = skb->data[hlen + 3] >> 6;
 
-       rate = ieee80211_get_tx_rate(sc->hw, info);
-       if (!rate) {
-               ret = -EINVAL;
-               goto err_unmap;
+               if (test_bit(keyix, common->keymap))
+                       return RX_FLAG_DECRYPTED;
        }
 
-       if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-               flags |= AR5K_TXDESC_NOACK;
+       return 0;
+}
 
-       rc_flags = info->control.rates[0].flags;
-       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
-               rate->hw_value_short : rate->hw_value;
 
-       pktlen = skb->len;
+static void
+ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
+                    struct ieee80211_rx_status *rxs)
+{
+       struct ath_common *common = ath5k_hw_common(sc->ah);
+       u64 tsf, bc_tstamp;
+       u32 hw_tu;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
 
-       /* FIXME: If we are in g mode and rate is a CCK rate
-        * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
-        * from tx power (value is in dB units already) */
-       if (info->control.hw_key) {
-               keyidx = info->control.hw_key->hw_key_idx;
-               pktlen += info->control.hw_key->icv_len;
-       }
-       if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
-               flags |= AR5K_TXDESC_RTSENA;
-               cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
-               duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
-                       sc->vif, pktlen, info));
-       }
-       if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               flags |= AR5K_TXDESC_CTSENA;
-               cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
-               duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
-                       sc->vif, pktlen, info));
-       }
-       ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
-               ieee80211_get_hdrlen_from_skb(skb), padsize,
-               get_hw_packet_type(skb),
-               (sc->power_level * 2),
-               hw_rate,
-               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
-               cts_rate, duration);
-       if (ret)
-               goto err_unmap;
+       if (ieee80211_is_beacon(mgmt->frame_control) &&
+           le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
+           memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
+               /*
+                * Received an IBSS beacon with the same BSSID. Hardware *must*
+                * have updated the local TSF. We have to work around various
+                * hardware bugs, though...
+                */
+               tsf = ath5k_hw_get_tsf64(sc->ah);
+               bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+               hw_tu = TSF_TO_TU(tsf);
 
-       memset(mrr_rate, 0, sizeof(mrr_rate));
-       memset(mrr_tries, 0, sizeof(mrr_tries));
-       for (i = 0; i < 3; i++) {
-               rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
-               if (!rate)
-                       break;
+               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                       "beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
+                       (unsigned long long)bc_tstamp,
+                       (unsigned long long)rxs->mactime,
+                       (unsigned long long)(rxs->mactime - bc_tstamp),
+                       (unsigned long long)tsf);
 
-               mrr_rate[i] = rate->hw_value;
-               mrr_tries[i] = info->control.rates[i + 1].count;
-       }
+               /*
+                * Sometimes the HW will give us a wrong tstamp in the rx
+                * status, causing the timestamp extension to go wrong.
+                * (This seems to happen especially with beacon frames bigger
+                * than 78 byte (incl. FCS))
+                * But we know that the receive timestamp must be later than the
+                * timestamp of the beacon since HW must have synced to that.
+                *
+                * NOTE: here we assume mactime to be after the frame was
+                * received, not like mac80211 which defines it at the start.
+                */
+               if (bc_tstamp > rxs->mactime) {
+                       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                               "fixing mactime from %llx to %llx\n",
+                               (unsigned long long)rxs->mactime,
+                               (unsigned long long)tsf);
+                       rxs->mactime = tsf;
+               }
 
-       ath5k_hw_setup_mrr_tx_desc(ah, ds,
-               mrr_rate[0], mrr_tries[0],
-               mrr_rate[1], mrr_tries[1],
-               mrr_rate[2], mrr_tries[2]);
+               /*
+                * Local TSF might have moved higher than our beacon timers,
+                * in that case we have to update them to continue sending
+                * beacons. This also takes care of synchronizing beacon sending
+                * times with other stations.
+                */
+               if (hw_tu >= sc->nexttbtt)
+                       ath5k_beacon_update_timers(sc, bc_tstamp);
+       }
+}
 
-       ds->ds_link = 0;
-       ds->ds_data = bf->skbaddr;
+static void
+ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
+{
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
 
-       spin_lock_bh(&txq->lock);
-       list_add_tail(&bf->list, &txq->q);
-       if (txq->link == NULL) /* is this first packet? */
-               ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
-       else /* no, so only link it */
-               *txq->link = bf->daddr;
+       /* only beacons from our BSSID */
+       if (!ieee80211_is_beacon(mgmt->frame_control) ||
+           memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
+               return;
 
-       txq->link = &ds->ds_link;
-       ath5k_hw_start_tx_dma(ah, txq->qnum);
-       mmiowb();
-       spin_unlock_bh(&txq->lock);
+       ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
+                                                     rssi);
 
-       return 0;
-err_unmap:
-       pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
-       return ret;
+       /* in IBSS mode we should keep RSSI statistics per neighbour */
+       /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
 }
 
-/*******************\
-* Descriptors setup *
-\*******************/
-
-static int
-ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
+/*
+ * Compute padding position. skb must contain an IEEE 802.11 frame
+ */
+static int ath5k_common_padpos(struct sk_buff *skb)
 {
-       struct ath5k_desc *ds;
-       struct ath5k_buf *bf;
-       dma_addr_t da;
-       unsigned int i;
-       int ret;
+       struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
+       __le16 frame_control = hdr->frame_control;
+       int padpos = 24;
 
-       /* allocate descriptors */
-       sc->desc_len = sizeof(struct ath5k_desc) *
-                       (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
-       sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
-       if (sc->desc == NULL) {
-               ATH5K_ERR(sc, "can't allocate descriptors\n");
-               ret = -ENOMEM;
-               goto err;
+       if (ieee80211_has_a4(frame_control)) {
+               padpos += ETH_ALEN;
        }
-       ds = sc->desc;
-       da = sc->desc_daddr;
-       ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
-               ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
+       if (ieee80211_is_data_qos(frame_control)) {
+               padpos += IEEE80211_QOS_CTL_LEN;
+       }
+
+       return padpos;
+}
+
+/*
+ * This function expects an 802.11 frame and returns the number of
+ * bytes added, or -1 if we don't have enough header room.
+ */
+static int ath5k_add_padding(struct sk_buff *skb)
+{
+       int padpos = ath5k_common_padpos(skb);
+       int padsize = padpos & 3;
 
-       bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
-                       sizeof(struct ath5k_buf), GFP_KERNEL);
-       if (bf == NULL) {
-               ATH5K_ERR(sc, "can't allocate bufptr\n");
-               ret = -ENOMEM;
-               goto err_free;
-       }
-       sc->bufptr = bf;
+       if (padsize && skb->len>padpos) {
 
-       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);
-       }
+               if (skb_headroom(skb) < padsize)
+                       return -1;
 
-       INIT_LIST_HEAD(&sc->txbuf);
-       sc->txbuf_len = ATH_TXBUF;
-       for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
-                       da += sizeof(*ds)) {
-               bf->desc = ds;
-               bf->daddr = da;
-               list_add_tail(&bf->list, &sc->txbuf);
+               skb_push(skb, padsize);
+               memmove(skb->data, skb->data+padsize, padpos);
+               return padsize;
        }
 
-       /* beacon buffer */
-       bf->desc = ds;
-       bf->daddr = da;
-       sc->bbuf = bf;
+       return 0;
+}
+
+/*
+ * The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = 4 - (hdrlen & 3); however, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit.  We must not try to
+ * remove padding from short control frames that do not have a
+ * payload.
+ *
+ * This function expects an 802.11 frame and returns the number of
+ * bytes removed.
+ */
+static int ath5k_remove_padding(struct sk_buff *skb)
+{
+       int padpos = ath5k_common_padpos(skb);
+       int padsize = padpos & 3;
+
+       if (padsize && skb->len>=padpos+padsize) {
+               memmove(skb->data + padsize, skb->data, padpos);
+               skb_pull(skb, padsize);
+               return padsize;
+       }
 
        return 0;
-err_free:
-       pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-err:
-       sc->desc = NULL;
-       return ret;
 }
 
 static void
-ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
+ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
+                   struct ath5k_rx_status *rs)
 {
-       struct ath5k_buf *bf;
+       struct ieee80211_rx_status *rxs;
 
-       ath5k_txbuf_free_skb(sc, sc->bbuf);
-       list_for_each_entry(bf, &sc->txbuf, list)
-               ath5k_txbuf_free_skb(sc, bf);
-       list_for_each_entry(bf, &sc->rxbuf, list)
-               ath5k_rxbuf_free_skb(sc, bf);
+       ath5k_remove_padding(skb);
 
-       /* Free memory associated with all descriptors */
-       pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-       sc->desc = NULL;
-       sc->desc_daddr = 0;
+       rxs = IEEE80211_SKB_RXCB(skb);
 
-       kfree(sc->bufptr);
-       sc->bufptr = NULL;
-       sc->bbuf = NULL;
-}
+       rxs->flag = 0;
+       if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
+               rxs->flag |= RX_FLAG_MMIC_ERROR;
 
+       /*
+        * always extend the mac timestamp, since this information is
+        * also needed for proper IBSS merging.
+        *
+        * XXX: it might be too late to do it here, since rs_tstamp is
+        * 15bit only. that means TSF extension has to be done within
+        * 32768usec (about 32ms). it might be necessary to move this to
+        * the interrupt handler, like it is done in madwifi.
+        *
+        * Unfortunately we don't know when the hardware takes the rx
+        * timestamp (beginning of phy frame, data frame, end of rx?).
+        * The only thing we know is that it is hardware specific...
+        * On AR5213 it seems the rx timestamp is at the end of the
+        * frame, but i'm not sure.
+        *
+        * NOTE: mac80211 defines mactime at the beginning of the first
+        * data symbol. Since we don't have any time references it's
+        * impossible to comply to that. This affects IBSS merge only
+        * right now, so it's not too bad...
+        */
+       rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
+       rxs->flag |= RX_FLAG_TSFT;
 
+       rxs->freq = sc->curchan->center_freq;
+       rxs->band = sc->curband->band;
 
+       rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
 
+       rxs->antenna = rs->rs_antenna;
 
-/**************\
-* Queues setup *
-\**************/
+       if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
+               sc->stats.antenna_rx[rs->rs_antenna]++;
+       else
+               sc->stats.antenna_rx[0]++; /* invalid */
 
-static struct ath5k_txq *
-ath5k_txq_setup(struct ath5k_softc *sc,
-               int qtype, int subtype)
-{
-       struct ath5k_hw *ah = sc->ah;
-       struct ath5k_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;
+       rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
+       rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
 
-       /*
-        * Enable interrupts only for EOL and DESC conditions.
-        * We mark tx descriptors to receive a DESC interrupt
-        * when a tx queue gets deep; otherwise we wait 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)) {
-               ATH5K_ERR(sc, "hw 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];
-}
+       if (rxs->rate_idx >= 0 && rs->rs_rate ==
+           sc->curband->bitrates[rxs->rate_idx].hw_value_short)
+               rxs->flag |= RX_FLAG_SHORTPRE;
 
-static int
-ath5k_beaconq_setup(struct ath5k_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
-       };
+       ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
 
-       return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
-}
+       ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
 
-static int
-ath5k_beaconq_config(struct ath5k_softc *sc)
-{
-       struct ath5k_hw *ah = sc->ah;
-       struct ath5k_txq_info qi;
-       int ret;
+       /* check beacons in IBSS mode */
+       if (sc->opmode == NL80211_IFTYPE_ADHOC)
+               ath5k_check_ibss_tsf(sc, skb, rxs);
 
-       ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
-       if (ret)
-               goto err;
+       ieee80211_rx(sc->hw, skb);
+}
 
-       if (sc->opmode == NL80211_IFTYPE_AP ||
-               sc->opmode == NL80211_IFTYPE_MESH_POINT) {
-               /*
-                * Always burst out beacon and CAB traffic
-                * (aifs = cwmin = cwmax = 0)
-                */
-               qi.tqi_aifs = 0;
-               qi.tqi_cw_min = 0;
-               qi.tqi_cw_max = 0;
-       } else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-               /*
-                * Adhoc mode; backoff between 0 and (2 * cw_min).
-                */
-               qi.tqi_aifs = 0;
-               qi.tqi_cw_min = 0;
-               qi.tqi_cw_max = 2 * ah->ah_cw_min;
-       }
+/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
+ *
+ * Check if we want to further process this frame or not. Also update
+ * statistics. Return true if we want this frame, false if not.
+ */
+static bool
+ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
+{
+       sc->stats.rx_all_count++;
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-               "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
-               qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
+       if (unlikely(rs->rs_status)) {
+               if (rs->rs_status & AR5K_RXERR_CRC)
+                       sc->stats.rxerr_crc++;
+               if (rs->rs_status & AR5K_RXERR_FIFO)
+                       sc->stats.rxerr_fifo++;
+               if (rs->rs_status & AR5K_RXERR_PHY) {
+                       sc->stats.rxerr_phy++;
+                       if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
+                               sc->stats.rxerr_phy_code[rs->rs_phyerr]++;
+                       return false;
+               }
+               if (rs->rs_status & 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
+                        */
+                       sc->stats.rxerr_decrypt++;
+                       if (rs->rs_keyix == AR5K_RXKEYIX_INVALID &&
+                           !(rs->rs_status & AR5K_RXERR_CRC))
+                               return true;
+               }
+               if (rs->rs_status & AR5K_RXERR_MIC) {
+                       sc->stats.rxerr_mic++;
+                       return true;
+               }
 
-       ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
-       if (ret) {
-               ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
-                       "hardware queue!\n", __func__);
-               goto err;
+               /* reject any frames with non-crypto errors */
+               if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
+                       return false;
        }
-       ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
-       if (ret)
-               goto err;
-
-       /* reconfigure cabq with ready time to 80% of beacon_interval */
-       ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
-       if (ret)
-               goto err;
-
-       qi.tqi_ready_time = (sc->bintval * 80) / 100;
-       ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
-       if (ret)
-               goto err;
 
-       ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
-err:
-       return ret;
+       if (unlikely(rs->rs_more)) {
+               sc->stats.rxerr_jumbo++;
+               return false;
+       }
+       return true;
 }
 
 static void
-ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+ath5k_tasklet_rx(unsigned long data)
 {
-       struct ath5k_buf *bf, *bf0;
+       struct ath5k_rx_status rs = {};
+       struct sk_buff *skb, *next_skb;
+       dma_addr_t next_skb_addr;
+       struct ath5k_softc *sc = (void *)data;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
+       struct ath5k_buf *bf;
+       struct ath5k_desc *ds;
+       int ret;
 
-       /*
-        * NB: this assumes output has been stopped and
-        *     we do not need to block ath5k_tx_tasklet
-        */
-       spin_lock_bh(&txq->lock);
-       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-               ath5k_debug_printtxbuf(sc, bf);
+       spin_lock(&sc->rxbuflock);
+       if (list_empty(&sc->rxbuf)) {
+               ATH5K_WARN(sc, "empty rx buf pool\n");
+               goto unlock;
+       }
+       do {
+               bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+               BUG_ON(bf->skb == NULL);
+               skb = bf->skb;
+               ds = bf->desc;
 
-               ath5k_txbuf_free_skb(sc, bf);
+               /* bail if HW is still using self-linked descriptor */
+               if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr)
+                       break;
 
-               spin_lock_bh(&sc->txbuflock);
-               list_move_tail(&bf->list, &sc->txbuf);
-               sc->txbuf_len++;
-               spin_unlock_bh(&sc->txbuflock);
-       }
-       txq->link = NULL;
-       spin_unlock_bh(&txq->lock);
-}
+               ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
+               if (unlikely(ret == -EINPROGRESS))
+                       break;
+               else if (unlikely(ret)) {
+                       ATH5K_ERR(sc, "error in processing rx descriptor\n");
+                       sc->stats.rxerr_proc++;
+                       break;
+               }
 
-/*
- * Drain the transmit queues and reclaim resources.
- */
-static void
-ath5k_txq_cleanup(struct ath5k_softc *sc)
-{
-       struct ath5k_hw *ah = sc->ah;
-       unsigned int i;
+               if (ath5k_receive_frame_ok(sc, &rs)) {
+                       next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr);
 
-       /* XXX return value */
-       if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
-               /* don't touch the hardware if marked invalid */
-               ath5k_hw_stop_tx_dma(ah, sc->bhalq);
-               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
-                       ath5k_hw_get_txdp(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);
-                               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
-                                       "link %p\n",
-                                       sc->txqs[i].qnum,
-                                       ath5k_hw_get_txdp(ah,
-                                                       sc->txqs[i].qnum),
-                                       sc->txqs[i].link);
-                       }
-       }
+                       /*
+                        * If we can't replace bf->skb with a new skb under
+                        * memory pressure, just skip this packet
+                        */
+                       if (!next_skb)
+                               goto next;
 
-       for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
-               if (sc->txqs[i].setup)
-                       ath5k_txq_drainq(sc, &sc->txqs[i]);
-}
+                       pci_unmap_single(sc->pdev, bf->skbaddr,
+                                        common->rx_bufsize,
+                                        PCI_DMA_FROMDEVICE);
 
-static void
-ath5k_txq_release(struct ath5k_softc *sc)
-{
-       struct ath5k_txq *txq = sc->txqs;
-       unsigned int i;
+                       skb_put(skb, rs.rs_datalen);
 
-       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;
+                       ath5k_receive_frame(sc, skb, &rs);
+
+                       bf->skb = next_skb;
+                       bf->skbaddr = next_skb_addr;
                }
+next:
+               list_move_tail(&bf->list, &sc->rxbuf);
+       } while (ath5k_rxbuf_setup(sc, bf) == 0);
+unlock:
+       spin_unlock(&sc->rxbuflock);
 }
 
 
-
-
 /*************\
-* RX Handling *
+* TX Handling *
 \*************/
 
-/*
- * Enable the receive h/w following a reset.
- */
-static int
-ath5k_rx_start(struct ath5k_softc *sc)
+static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+                         struct ath5k_txq *txq)
 {
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
+       struct ath5k_softc *sc = hw->priv;
        struct ath5k_buf *bf;
-       int ret;
+       unsigned long flags;
+       int padsize;
 
-       common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
+       ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
-                 common->cachelsz, common->rx_bufsize);
+       /*
+        * The hardware expects the header padded to 4 byte boundaries.
+        * If this is not the case, we add the padding after the header.
+        */
+       padsize = ath5k_add_padding(skb);
+       if (padsize < 0) {
+               ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
+                         " headroom to pad");
+               goto drop_packet;
+       }
 
-       spin_lock_bh(&sc->rxbuflock);
-       sc->rxlink = NULL;
-       list_for_each_entry(bf, &sc->rxbuf, list) {
-               ret = ath5k_rxbuf_setup(sc, bf);
-               if (ret != 0) {
-                       spin_unlock_bh(&sc->rxbuflock);
-                       goto err;
-               }
+       if (txq->txq_len >= ATH5K_TXQ_LEN_MAX)
+               ieee80211_stop_queue(hw, txq->qnum);
+
+       spin_lock_irqsave(&sc->txbuflock, flags);
+       if (list_empty(&sc->txbuf)) {
+               ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
+               spin_unlock_irqrestore(&sc->txbuflock, flags);
+               ieee80211_stop_queues(hw);
+               goto drop_packet;
        }
-       bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
-       ath5k_hw_set_rxdp(ah, bf->daddr);
-       spin_unlock_bh(&sc->rxbuflock);
+       bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
+       list_del(&bf->list);
+       sc->txbuf_len--;
+       if (list_empty(&sc->txbuf))
+               ieee80211_stop_queues(hw);
+       spin_unlock_irqrestore(&sc->txbuflock, flags);
 
-       ath5k_hw_start_rx_dma(ah);      /* enable recv descriptors */
-       ath5k_mode_setup(sc);           /* set filters, etc. */
-       ath5k_hw_start_rx_pcu(ah);      /* re-enable PCU/DMA engine */
+       bf->skb = skb;
 
-       return 0;
-err:
-       return ret;
+       if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
+               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);
+               goto drop_packet;
+       }
+       return NETDEV_TX_OK;
+
+drop_packet:
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
 }
 
-/*
- * Disable the receive h/w in preparation for a reset.
- */
 static void
-ath5k_rx_stop(struct ath5k_softc *sc)
+ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
+                        struct ath5k_tx_status *ts)
 {
-       struct ath5k_hw *ah = sc->ah;
-
-       ath5k_hw_stop_rx_pcu(ah);       /* disable PCU */
-       ath5k_hw_set_rx_filter(ah, 0);  /* clear recv filter */
-       ath5k_hw_stop_rx_dma(ah);       /* disable DMA engine */
+       struct ieee80211_tx_info *info;
+       int i;
 
-       ath5k_debug_printrxbuffs(sc, ah);
-}
+       sc->stats.tx_all_count++;
+       info = IEEE80211_SKB_CB(skb);
 
-static unsigned int
-ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
-                  struct ath5k_rx_status *rs)
-{
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
-       struct ieee80211_hdr *hdr = (void *)skb->data;
-       unsigned int keyix, hlen;
+       ieee80211_tx_info_clear_status(info);
+       for (i = 0; i < 4; i++) {
+               struct ieee80211_tx_rate *r =
+                       &info->status.rates[i];
 
-       if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
-                       rs->rs_keyix != AR5K_RXKEYIX_INVALID)
-               return RX_FLAG_DECRYPTED;
+               if (ts->ts_rate[i]) {
+                       r->idx = ath5k_hw_to_driver_rix(sc, ts->ts_rate[i]);
+                       r->count = ts->ts_retry[i];
+               } else {
+                       r->idx = -1;
+                       r->count = 0;
+               }
+       }
 
-       /* Apparently when a default key is used to decrypt the packet
-          the hw does not set the index used to decrypt.  In such cases
-          get the index from the packet. */
-       hlen = ieee80211_hdrlen(hdr->frame_control);
-       if (ieee80211_has_protected(hdr->frame_control) &&
-           !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
-           skb->len >= hlen + 4) {
-               keyix = skb->data[hlen + 3] >> 6;
+       /* count the successful attempt as well */
+       info->status.rates[ts->ts_final_idx].count++;
 
-               if (test_bit(keyix, common->keymap))
-                       return RX_FLAG_DECRYPTED;
+       if (unlikely(ts->ts_status)) {
+               sc->stats.ack_fail++;
+               if (ts->ts_status & AR5K_TXERR_FILT) {
+                       info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+                       sc->stats.txerr_filt++;
+               }
+               if (ts->ts_status & AR5K_TXERR_XRETRY)
+                       sc->stats.txerr_retry++;
+               if (ts->ts_status & AR5K_TXERR_FIFO)
+                       sc->stats.txerr_fifo++;
+       } else {
+               info->flags |= IEEE80211_TX_STAT_ACK;
+               info->status.ack_signal = ts->ts_rssi;
        }
 
-       return 0;
-}
+       /*
+       * Remove MAC header padding before giving the frame
+       * back to mac80211.
+       */
+       ath5k_remove_padding(skb);
+
+       if (ts->ts_antenna > 0 && ts->ts_antenna < 5)
+               sc->stats.antenna_tx[ts->ts_antenna]++;
+       else
+               sc->stats.antenna_tx[0]++; /* invalid */
 
+       ieee80211_tx_status(sc->hw, skb);
+}
 
 static void
-ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
-                    struct ieee80211_rx_status *rxs)
+ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 {
-       struct ath_common *common = ath5k_hw_common(sc->ah);
-       u64 tsf, bc_tstamp;
-       u32 hw_tu;
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct ath5k_tx_status ts = {};
+       struct ath5k_buf *bf, *bf0;
+       struct ath5k_desc *ds;
+       struct sk_buff *skb;
+       int ret;
 
-       if (ieee80211_is_beacon(mgmt->frame_control) &&
-           le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
-           memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
-               /*
-                * Received an IBSS beacon with the same BSSID. Hardware *must*
-                * have updated the local TSF. We have to work around various
-                * hardware bugs, though...
-                */
-               tsf = ath5k_hw_get_tsf64(sc->ah);
-               bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
-               hw_tu = TSF_TO_TU(tsf);
+       spin_lock(&txq->lock);
+       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
 
-               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                       "beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
-                       (unsigned long long)bc_tstamp,
-                       (unsigned long long)rxs->mactime,
-                       (unsigned long long)(rxs->mactime - bc_tstamp),
-                       (unsigned long long)tsf);
+               txq->txq_poll_mark = false;
 
-               /*
-                * Sometimes the HW will give us a wrong tstamp in the rx
-                * status, causing the timestamp extension to go wrong.
-                * (This seems to happen especially with beacon frames bigger
-                * than 78 byte (incl. FCS))
-                * But we know that the receive timestamp must be later than the
-                * timestamp of the beacon since HW must have synced to that.
-                *
-                * NOTE: here we assume mactime to be after the frame was
-                * received, not like mac80211 which defines it at the start.
-                */
-               if (bc_tstamp > rxs->mactime) {
-                       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                               "fixing mactime from %llx to %llx\n",
-                               (unsigned long long)rxs->mactime,
-                               (unsigned long long)tsf);
-                       rxs->mactime = tsf;
+               /* skb might already have been processed last time. */
+               if (bf->skb != NULL) {
+                       ds = bf->desc;
+
+                       ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
+                       if (unlikely(ret == -EINPROGRESS))
+                               break;
+                       else if (unlikely(ret)) {
+                               ATH5K_ERR(sc,
+                                       "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);
+                       ath5k_tx_frame_completed(sc, skb, &ts);
                }
 
                /*
-                * Local TSF might have moved higher than our beacon timers,
-                * in that case we have to update them to continue sending
-                * beacons. This also takes care of synchronizing beacon sending
-                * times with other stations.
+                * It's possible that the hardware can say the buffer is
+                * completed when it hasn't yet loaded the ds_link from
+                * host memory and moved on.
+                * Always keep the last descriptor to avoid HW races...
                 */
-               if (hw_tu >= sc->nexttbtt)
-                       ath5k_beacon_update_timers(sc, bc_tstamp);
+               if (ath5k_hw_get_txdp(sc->ah, txq->qnum) != bf->daddr) {
+                       spin_lock(&sc->txbuflock);
+                       list_move_tail(&bf->list, &sc->txbuf);
+                       sc->txbuf_len++;
+                       txq->txq_len--;
+                       spin_unlock(&sc->txbuflock);
+               }
        }
+       spin_unlock(&txq->lock);
+       if (txq->txq_len < ATH5K_TXQ_LEN_LOW)
+               ieee80211_wake_queue(sc->hw, txq->qnum);
 }
 
 static void
-ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
+ath5k_tasklet_tx(unsigned long data)
 {
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       int i;
+       struct ath5k_softc *sc = (void *)data;
+
+       for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
+               if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
+                       ath5k_tx_processq(sc, &sc->txqs[i]);
+}
+
+
+/*****************\
+* Beacon handling *
+\*****************/
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static int
+ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+       struct sk_buff *skb = bf->skb;
+       struct  ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
+       struct ath5k_desc *ds;
+       int ret = 0;
+       u8 antenna;
+       u32 flags;
+       const int padsize = 0;
 
-       /* only beacons from our BSSID */
-       if (!ieee80211_is_beacon(mgmt->frame_control) ||
-           memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
-               return;
+       bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+                       PCI_DMA_TODEVICE);
+       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] "
+                       "skbaddr %llx\n", skb, skb->data, skb->len,
+                       (unsigned long long)bf->skbaddr);
+       if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) {
+               ATH5K_ERR(sc, "beacon DMA mapping failed\n");
+               return -EIO;
+       }
 
-       ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
-                                                     rssi);
+       ds = bf->desc;
+       antenna = ah->ah_tx_ant;
 
-       /* in IBSS mode we should keep RSSI statistics per neighbour */
-       /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
+       flags = AR5K_TXDESC_NOACK;
+       if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
+               ds->ds_link = bf->daddr;        /* self-linked */
+               flags |= AR5K_TXDESC_VEOL;
+       } else
+               ds->ds_link = 0;
+
+       /*
+        * If we use multiple antennas on AP and use
+        * the Sectored AP scenario, switch antenna every
+        * 4 beacons to make sure everybody hears our AP.
+        * When a client tries to associate, hw will keep
+        * track of the tx antenna to be used for this client
+        * automaticaly, based on ACKed packets.
+        *
+        * Note: AP still listens and transmits RTS on the
+        * default antenna which is supposed to be an omni.
+        *
+        * Note2: On sectored scenarios it's possible to have
+        * multiple antennas (1 omni -- the default -- and 14
+        * sectors), so if we choose to actually support this
+        * mode, we need to allow the user to set how many antennas
+        * we have and tweak the code below to send beacons
+        * on all of them.
+        */
+       if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
+               antenna = sc->bsent & 4 ? 2 : 1;
+
+
+       /* FIXME: If we are in g mode and rate is a CCK rate
+        * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+        * from tx power (value is in dB units already) */
+       ds->ds_data = bf->skbaddr;
+       ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
+                       ieee80211_get_hdrlen_from_skb(skb), padsize,
+                       AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
+                       ieee80211_get_tx_rate(sc->hw, info)->hw_value,
+                       1, AR5K_TXKEYIX_INVALID,
+                       antenna, flags, 0, 0);
+       if (ret)
+               goto err_unmap;
+
+       return 0;
+err_unmap:
+       pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+       return ret;
 }
 
 /*
- * Compute padding position. skb must contain an IEEE 802.11 frame
+ * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
+ * this is called only once at config_bss time, for AP we do it every
+ * SWBA interrupt so that the TIM will reflect buffered frames.
+ *
+ * Called with the beacon lock.
  */
-static int ath5k_common_padpos(struct sk_buff *skb)
+static int
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
-       struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
-       __le16 frame_control = hdr->frame_control;
-       int padpos = 24;
+       int ret;
+       struct ath5k_softc *sc = hw->priv;
+       struct sk_buff *skb;
 
-       if (ieee80211_has_a4(frame_control)) {
-               padpos += ETH_ALEN;
+       if (WARN_ON(!vif)) {
+               ret = -EINVAL;
+               goto out;
        }
-       if (ieee80211_is_data_qos(frame_control)) {
-               padpos += IEEE80211_QOS_CTL_LEN;
+
+       skb = ieee80211_beacon_get(hw, vif);
+
+       if (!skb) {
+               ret = -ENOMEM;
+               goto out;
        }
 
-       return padpos;
+       ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
+
+       ath5k_txbuf_free_skb(sc, sc->bbuf);
+       sc->bbuf->skb = skb;
+       ret = ath5k_beacon_setup(sc, sc->bbuf);
+       if (ret)
+               sc->bbuf->skb = NULL;
+out:
+       return ret;
 }
 
 /*
- * This function expects an 802.11 frame and returns the number of
- * bytes added, or -1 if we don't have enough header room.
+ * 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 called from software irq context (beacontq tasklets)
+ * or user context from ath5k_beacon_config.
  */
-static int ath5k_add_padding(struct sk_buff *skb)
+static void
+ath5k_beacon_send(struct ath5k_softc *sc)
 {
-       int padpos = ath5k_common_padpos(skb);
-       int padsize = padpos & 3;
+       struct ath5k_buf *bf = sc->bbuf;
+       struct ath5k_hw *ah = sc->ah;
+       struct sk_buff *skb;
+
+       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
+
+       if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) {
+               ATH5K_WARN(sc, "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++;
+               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+                       "missed %u consecutive beacons\n", sc->bmisscount);
+               if (sc->bmisscount > 10) {      /* NB: 10 is a guess */
+                       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+                               "stuck beacon time (%u missed)\n",
+                               sc->bmisscount);
+                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                                 "stuck beacon, resetting\n");
+                       ieee80211_queue_work(sc->hw, &sc->reset_work);
+               }
+               return;
+       }
+       if (unlikely(sc->bmisscount != 0)) {
+               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+                       "resume beacon xmit after %u misses\n",
+                       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))) {
+               ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq);
+               /* NB: hw still stops DMA, so proceed */
+       }
 
-       if (padsize && skb->len>padpos) {
+       /* refresh the beacon for AP mode */
+       if (sc->opmode == NL80211_IFTYPE_AP)
+               ath5k_beacon_update(sc->hw, sc->vif);
 
-               if (skb_headroom(skb) < padsize)
-                       return -1;
+       ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
+       ath5k_hw_start_tx_dma(ah, sc->bhalq);
+       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
+               sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
 
-               skb_push(skb, padsize);
-               memmove(skb->data, skb->data+padsize, padpos);
-               return padsize;
+       skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
+       while (skb) {
+               ath5k_tx_queue(sc->hw, skb, sc->cabq);
+               skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
        }
 
-       return 0;
+       sc->bsent++;
 }
 
-/*
- * The MAC header is padded to have 32-bit boundary if the
- * packet payload is non-zero. The general calculation for
- * padsize would take into account odd header lengths:
- * padsize = 4 - (hdrlen & 3); however, since only
- * even-length headers are used, padding can only be 0 or 2
- * bytes and we can optimize this a bit.  We must not try to
- * remove padding from short control frames that do not have a
- * payload.
+/**
+ * ath5k_beacon_update_timers - update beacon timers
  *
- * This function expects an 802.11 frame and returns the number of
- * bytes removed.
+ * @sc: struct ath5k_softc pointer we are operating on
+ * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a
+ *          beacon timer update based on the current HW TSF.
+ *
+ * Calculate the next target beacon transmit time (TBTT) based on the timestamp
+ * of a received beacon or the current local hardware TSF and write it to the
+ * beacon timer registers.
+ *
+ * This is called in a variety of situations, e.g. when a beacon is received,
+ * when a TSF update has been detected, but also when an new IBSS is created or
+ * when we otherwise know we have to update the timers, but we keep it in this
+ * function to have it all together in one place.
  */
-static int ath5k_remove_padding(struct sk_buff *skb)
+static void
+ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
 {
-       int padpos = ath5k_common_padpos(skb);
-       int padsize = padpos & 3;
+       struct ath5k_hw *ah = sc->ah;
+       u32 nexttbtt, intval, hw_tu, bc_tu;
+       u64 hw_tsf;
 
-       if (padsize && skb->len>=padpos+padsize) {
-               memmove(skb->data + padsize, skb->data, padpos);
-               skb_pull(skb, padsize);
-               return padsize;
-       }
+       intval = sc->bintval & AR5K_BEACON_PERIOD;
+       if (WARN_ON(!intval))
+               return;
 
-       return 0;
-}
+       /* beacon TSF converted to TU */
+       bc_tu = TSF_TO_TU(bc_tsf);
 
-static void
-ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
-                   struct ath5k_rx_status *rs)
-{
-       struct ieee80211_rx_status *rxs;
+       /* current TSF converted to TU */
+       hw_tsf = ath5k_hw_get_tsf64(ah);
+       hw_tu = TSF_TO_TU(hw_tsf);
 
-       ath5k_remove_padding(skb);
+#define FUDGE 3
+       /* we use FUDGE to make sure the next TBTT is ahead of the current TU */
+       if (bc_tsf == -1) {
+               /*
+                * no beacons received, called internally.
+                * just need to refresh timers based on HW TSF.
+                */
+               nexttbtt = roundup(hw_tu + FUDGE, intval);
+       } else if (bc_tsf == 0) {
+               /*
+                * no beacon received, probably called by ath5k_reset_tsf().
+                * reset TSF to start with 0.
+                */
+               nexttbtt = intval;
+               intval |= AR5K_BEACON_RESET_TSF;
+       } else if (bc_tsf > hw_tsf) {
+               /*
+                * beacon received, SW merge happend but HW TSF not yet updated.
+                * not possible to reconfigure timers yet, but next time we
+                * receive a beacon with the same BSSID, the hardware will
+                * automatically update the TSF and then we need to reconfigure
+                * the timers.
+                */
+               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                       "need to wait for HW TSF sync\n");
+               return;
+       } else {
+               /*
+                * most important case for beacon synchronization between STA.
+                *
+                * beacon received and HW TSF has been already updated by HW.
+                * update next TBTT based on the TSF of the beacon, but make
+                * sure it is ahead of our local TSF timer.
+                */
+               nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval);
+       }
+#undef FUDGE
 
-       rxs = IEEE80211_SKB_RXCB(skb);
+       sc->nexttbtt = nexttbtt;
 
-       rxs->flag = 0;
-       if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
-               rxs->flag |= RX_FLAG_MMIC_ERROR;
+       intval |= AR5K_BEACON_ENA;
+       ath5k_hw_init_beacon(ah, nexttbtt, intval);
 
        /*
-        * always extend the mac timestamp, since this information is
-        * also needed for proper IBSS merging.
-        *
-        * XXX: it might be too late to do it here, since rs_tstamp is
-        * 15bit only. that means TSF extension has to be done within
-        * 32768usec (about 32ms). it might be necessary to move this to
-        * the interrupt handler, like it is done in madwifi.
-        *
-        * Unfortunately we don't know when the hardware takes the rx
-        * timestamp (beginning of phy frame, data frame, end of rx?).
-        * The only thing we know is that it is hardware specific...
-        * On AR5213 it seems the rx timestamp is at the end of the
-        * frame, but i'm not sure.
-        *
-        * NOTE: mac80211 defines mactime at the beginning of the first
-        * data symbol. Since we don't have any time references it's
-        * impossible to comply to that. This affects IBSS merge only
-        * right now, so it's not too bad...
+        * debugging output last in order to preserve the time critical aspect
+        * of this function
         */
-       rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
-       rxs->flag |= RX_FLAG_TSFT;
-
-       rxs->freq = sc->curchan->center_freq;
-       rxs->band = sc->curband->band;
-
-       rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
-
-       rxs->antenna = rs->rs_antenna;
-
-       if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
-               sc->stats.antenna_rx[rs->rs_antenna]++;
+       if (bc_tsf == -1)
+               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                       "reconfigured timers based on HW TSF\n");
+       else if (bc_tsf == 0)
+               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                       "reset HW TSF and timers\n");
        else
-               sc->stats.antenna_rx[0]++; /* invalid */
-
-       rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
-       rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
-
-       if (rxs->rate_idx >= 0 && rs->rs_rate ==
-           sc->curband->bitrates[rxs->rate_idx].hw_value_short)
-               rxs->flag |= RX_FLAG_SHORTPRE;
-
-       ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
-
-       ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
-
-       /* check beacons in IBSS mode */
-       if (sc->opmode == NL80211_IFTYPE_ADHOC)
-               ath5k_check_ibss_tsf(sc, skb, rxs);
+               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                       "updated timers based on beacon TSF\n");
 
-       ieee80211_rx(sc->hw, skb);
+       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+                         "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n",
+                         (unsigned long long) bc_tsf,
+                         (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt);
+       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "intval %u %s %s\n",
+               intval & AR5K_BEACON_PERIOD,
+               intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "",
+               intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
 }
 
-/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
+/**
+ * ath5k_beacon_config - Configure the beacon queues and interrupts
  *
- * Check if we want to further process this frame or not. Also update
- * statistics. Return true if we want this frame, false if not.
+ * @sc: struct ath5k_softc pointer we are operating on
+ *
+ * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
+ * interrupts to detect TSF updates only.
  */
-static bool
-ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
+static void
+ath5k_beacon_config(struct ath5k_softc *sc)
 {
-       sc->stats.rx_all_count++;
+       struct ath5k_hw *ah = sc->ah;
+       unsigned long flags;
 
-       if (unlikely(rs->rs_status)) {
-               if (rs->rs_status & AR5K_RXERR_CRC)
-                       sc->stats.rxerr_crc++;
-               if (rs->rs_status & AR5K_RXERR_FIFO)
-                       sc->stats.rxerr_fifo++;
-               if (rs->rs_status & AR5K_RXERR_PHY) {
-                       sc->stats.rxerr_phy++;
-                       if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
-                               sc->stats.rxerr_phy_code[rs->rs_phyerr]++;
-                       return false;
-               }
-               if (rs->rs_status & 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
-                        */
-                       sc->stats.rxerr_decrypt++;
-                       if (rs->rs_keyix == AR5K_RXKEYIX_INVALID &&
-                           !(rs->rs_status & AR5K_RXERR_CRC))
-                               return true;
-               }
-               if (rs->rs_status & AR5K_RXERR_MIC) {
-                       sc->stats.rxerr_mic++;
-                       return true;
-               }
+       spin_lock_irqsave(&sc->block, flags);
+       sc->bmisscount = 0;
+       sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
 
-               /* reject any frames with non-crypto errors */
-               if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
-                       return false;
+       if (sc->enable_beacon) {
+               /*
+                * In IBSS mode we use a self-linked tx descriptor and let the
+                * hardware send the beacons automatically. We have to load it
+                * only once here.
+                * We use the SWBA interrupt only to keep track of the beacon
+                * timers in order to detect automatic TSF updates.
+                */
+               ath5k_beaconq_config(sc);
+
+               sc->imask |= AR5K_INT_SWBA;
+
+               if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+                       if (ath5k_hw_hasveol(ah))
+                               ath5k_beacon_send(sc);
+               } else
+                       ath5k_beacon_update_timers(sc, -1);
+       } else {
+               ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq);
        }
 
-       if (unlikely(rs->rs_more)) {
-               sc->stats.rxerr_jumbo++;
-               return false;
+       ath5k_hw_set_imr(ah, sc->imask);
+       mmiowb();
+       spin_unlock_irqrestore(&sc->block, flags);
+}
+
+static void ath5k_tasklet_beacon(unsigned long data)
+{
+       struct ath5k_softc *sc = (struct ath5k_softc *) data;
+
+       /*
+        * Software beacon alert--time to send a beacon.
+        *
+        * In IBSS mode we use this interrupt just to
+        * keep track of the next TBTT (target beacon
+        * transmission time) in order to detect wether
+        * automatic TSF updates happened.
+        */
+       if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+               /* XXX: only if VEOL suppported */
+               u64 tsf = ath5k_hw_get_tsf64(sc->ah);
+               sc->nexttbtt += sc->bintval;
+               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+                               "SWBA nexttbtt: %x hw_tu: %x "
+                               "TSF: %llx\n",
+                               sc->nexttbtt,
+                               TSF_TO_TU(tsf),
+                               (unsigned long long) tsf);
+       } else {
+               spin_lock(&sc->block);
+               ath5k_beacon_send(sc);
+               spin_unlock(&sc->block);
        }
-       return true;
 }
 
+
+/********************\
+* Interrupt handling *
+\********************/
+
 static void
-ath5k_tasklet_rx(unsigned long data)
+ath5k_intr_calibration_poll(struct ath5k_hw *ah)
 {
-       struct ath5k_rx_status rs = {};
-       struct sk_buff *skb, *next_skb;
-       dma_addr_t next_skb_addr;
-       struct ath5k_softc *sc = (void *)data;
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
-       struct ath5k_buf *bf;
-       struct ath5k_desc *ds;
-       int ret;
+       if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
+           !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
+               /* run ANI only when full calibration is not active */
+               ah->ah_cal_next_ani = jiffies +
+                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
+               tasklet_schedule(&ah->ah_sc->ani_tasklet);
 
-       spin_lock(&sc->rxbuflock);
-       if (list_empty(&sc->rxbuf)) {
-               ATH5K_WARN(sc, "empty rx buf pool\n");
-               goto unlock;
+       } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
+               ah->ah_cal_next_full = jiffies +
+                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
+               tasklet_schedule(&ah->ah_sc->calib);
        }
-       do {
-               bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
-               BUG_ON(bf->skb == NULL);
-               skb = bf->skb;
-               ds = bf->desc;
-
-               /* bail if HW is still using self-linked descriptor */
-               if (ath5k_hw_get_rxdp(sc->ah) == bf->daddr)
-                       break;
+       /* we could use SWI to generate enough interrupts to meet our
+        * calibration interval requirements, if necessary:
+        * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
+}
 
-               ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
-               if (unlikely(ret == -EINPROGRESS))
-                       break;
-               else if (unlikely(ret)) {
-                       ATH5K_ERR(sc, "error in processing rx descriptor\n");
-                       sc->stats.rxerr_proc++;
-                       break;
-               }
+static irqreturn_t
+ath5k_intr(int irq, void *dev_id)
+{
+       struct ath5k_softc *sc = dev_id;
+       struct ath5k_hw *ah = sc->ah;
+       enum ath5k_int status;
+       unsigned int counter = 1000;
 
-               if (ath5k_receive_frame_ok(sc, &rs)) {
-                       next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr);
+       if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
+                               !ath5k_hw_is_intr_pending(ah)))
+               return IRQ_NONE;
 
+       do {
+               ath5k_hw_get_isr(ah, &status);          /* NB: clears IRQ too */
+               ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
+                               status, sc->imask);
+               if (unlikely(status & AR5K_INT_FATAL)) {
                        /*
-                        * If we can't replace bf->skb with a new skb under
-                        * memory pressure, just skip this packet
+                        * Fatal errors are unrecoverable.
+                        * Typically these are caused by DMA errors.
                         */
-                       if (!next_skb)
-                               goto next;
+                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                                 "fatal int, resetting\n");
+                       ieee80211_queue_work(sc->hw, &sc->reset_work);
+               } else if (unlikely(status & AR5K_INT_RXORN)) {
+                       /*
+                        * Receive buffers are full. Either the bus is busy or
+                        * the CPU is not fast enough to process all received
+                        * frames.
+                        * Older chipsets need a reset to come out of this
+                        * condition, but we treat it as RX for newer chips.
+                        * We don't know exactly which versions need a reset -
+                        * this guess is copied from the HAL.
+                        */
+                       sc->stats.rxorn_intr++;
+                       if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
+                               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                                         "rx overrun, resetting\n");
+                               ieee80211_queue_work(sc->hw, &sc->reset_work);
+                       }
+                       else
+                               tasklet_schedule(&sc->rxtq);
+               } else {
+                       if (status & AR5K_INT_SWBA) {
+                               tasklet_hi_schedule(&sc->beacontq);
+                       }
+                       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->stats.rxeol_intr++;
+                       }
+                       if (status & AR5K_INT_TXURN) {
+                               /* bump tx trigger level */
+                               ath5k_hw_update_tx_triglevel(ah, true);
+                       }
+                       if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
+                               tasklet_schedule(&sc->rxtq);
+                       if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+                                       | AR5K_INT_TXERR | AR5K_INT_TXEOL))
+                               tasklet_schedule(&sc->txtq);
+                       if (status & AR5K_INT_BMISS) {
+                               /* TODO */
+                       }
+                       if (status & AR5K_INT_MIB) {
+                               sc->stats.mib_intr++;
+                               ath5k_hw_update_mib_counters(ah);
+                               ath5k_ani_mib_intr(ah);
+                       }
+                       if (status & AR5K_INT_GPIO)
+                               tasklet_schedule(&sc->rf_kill.toggleq);
 
-                       pci_unmap_single(sc->pdev, bf->skbaddr,
-                                        common->rx_bufsize,
-                                        PCI_DMA_FROMDEVICE);
+               }
+       } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
-                       skb_put(skb, rs.rs_datalen);
+       if (unlikely(!counter))
+               ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
 
-                       ath5k_receive_frame(sc, skb, &rs);
+       ath5k_intr_calibration_poll(ah);
 
-                       bf->skb = next_skb;
-                       bf->skbaddr = next_skb_addr;
-               }
-next:
-               list_move_tail(&bf->list, &sc->rxbuf);
-       } while (ath5k_rxbuf_setup(sc, bf) == 0);
-unlock:
-       spin_unlock(&sc->rxbuflock);
+       return IRQ_HANDLED;
 }
 
-
-/*************\
-* TX Handling *
-\*************/
-
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
 static void
-ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+ath5k_tasklet_calibrate(unsigned long data)
 {
-       struct ath5k_tx_status ts = {};
-       struct ath5k_buf *bf, *bf0;
-       struct ath5k_desc *ds;
-       struct sk_buff *skb;
-       struct ieee80211_tx_info *info;
-       int i, ret;
+       struct ath5k_softc *sc = (void *)data;
+       struct ath5k_hw *ah = sc->ah;
 
-       spin_lock(&txq->lock);
-       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-               ds = bf->desc;
+       /* Only full calibration for now */
+       ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
+
+       ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
+               ieee80211_frequency_to_channel(sc->curchan->center_freq),
+               sc->curchan->hw_value);
 
+       if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
                /*
-                * It's possible that the hardware can say the buffer is
-                * completed when it hasn't yet loaded the ds_link from
-                * host memory and moved on.  If there are more TX
-                * descriptors in the queue, wait for TXDP to change
-                * before processing this one.
+                * Rfgain is out of bounds, reset the chip
+                * to load new gain values.
                 */
-               if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
-                   !list_is_last(&bf->list, &txq->q))
-                       break;
-
-               ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
-               if (unlikely(ret == -EINPROGRESS))
-                       break;
-               else if (unlikely(ret)) {
-                       ATH5K_ERR(sc, "error %d while processing queue %u\n",
-                               ret, txq->qnum);
-                       break;
-               }
-
-               sc->stats.tx_all_count++;
-               skb = bf->skb;
-               info = IEEE80211_SKB_CB(skb);
-               bf->skb = NULL;
-
-               pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
-                               PCI_DMA_TODEVICE);
-
-               ieee80211_tx_info_clear_status(info);
-               for (i = 0; i < 4; i++) {
-                       struct ieee80211_tx_rate *r =
-                               &info->status.rates[i];
-
-                       if (ts.ts_rate[i]) {
-                               r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
-                               r->count = ts.ts_retry[i];
-                       } else {
-                               r->idx = -1;
-                               r->count = 0;
-                       }
-               }
-
-               /* count the successful attempt as well */
-               info->status.rates[ts.ts_final_idx].count++;
+               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
+               ieee80211_queue_work(sc->hw, &sc->reset_work);
+       }
+       if (ath5k_hw_phy_calibrate(ah, sc->curchan))
+               ATH5K_ERR(sc, "calibration of channel %u failed\n",
+                       ieee80211_frequency_to_channel(
+                               sc->curchan->center_freq));
 
-               if (unlikely(ts.ts_status)) {
-                       sc->stats.ack_fail++;
-                       if (ts.ts_status & AR5K_TXERR_FILT) {
-                               info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
-                               sc->stats.txerr_filt++;
-                       }
-                       if (ts.ts_status & AR5K_TXERR_XRETRY)
-                               sc->stats.txerr_retry++;
-                       if (ts.ts_status & AR5K_TXERR_FIFO)
-                               sc->stats.txerr_fifo++;
-               } else {
-                       info->flags |= IEEE80211_TX_STAT_ACK;
-                       info->status.ack_signal = ts.ts_rssi;
-               }
+       /* Noise floor calibration interrupts rx/tx path while I/Q calibration
+        * doesn't.
+        * TODO: We should stop TX here, so that it doesn't interfere.
+        * Note that stopping the queues is not enough to stop TX! */
+       if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
+               ah->ah_cal_next_nf = jiffies +
+                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
+               ath5k_hw_update_noise_floor(ah);
+       }
 
-               /*
-                * Remove MAC header padding before giving the frame
-                * back to mac80211.
-                */
-               ath5k_remove_padding(skb);
+       ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
+}
 
-               if (ts.ts_antenna > 0 && ts.ts_antenna < 5)
-                       sc->stats.antenna_tx[ts.ts_antenna]++;
-               else
-                       sc->stats.antenna_tx[0]++; /* invalid */
 
-               ieee80211_tx_status(sc->hw, skb);
+static void
+ath5k_tasklet_ani(unsigned long data)
+{
+       struct ath5k_softc *sc = (void *)data;
+       struct ath5k_hw *ah = sc->ah;
 
-               spin_lock(&sc->txbuflock);
-               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);
+       ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
+       ath5k_ani_calibration(ah);
+       ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
 }
 
+
 static void
-ath5k_tasklet_tx(unsigned long data)
+ath5k_tx_complete_poll_work(struct work_struct *work)
 {
+       struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+                       tx_complete_work.work);
+       struct ath5k_txq *txq;
        int i;
-       struct ath5k_softc *sc = (void *)data;
+       bool needreset = false;
+
+       for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
+               if (sc->txqs[i].setup) {
+                       txq = &sc->txqs[i];
+                       spin_lock_bh(&txq->lock);
+                       if (txq->txq_len > 1) {
+                               if (txq->txq_poll_mark) {
+                                       ATH5K_DBG(sc, ATH5K_DEBUG_XMIT,
+                                                 "TX queue stuck %d\n",
+                                                 txq->qnum);
+                                       needreset = true;
+                                       txq->txq_stuck++;
+                                       spin_unlock_bh(&txq->lock);
+                                       break;
+                               } else {
+                                       txq->txq_poll_mark = true;
+                               }
+                       }
+                       spin_unlock_bh(&txq->lock);
+               }
+       }
 
-       for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
-               if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
-                       ath5k_tx_processq(sc, &sc->txqs[i]);
+       if (needreset) {
+               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                         "TX queues stuck, resetting\n");
+               ath5k_reset(sc, sc->curchan);
+       }
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+               msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
 }
 
 
-/*****************\
-* Beacon handling *
-\*****************/
+/*************************\
+* Initialization routines *
+\*************************/
 
-/*
- * Setup the beacon frame for transmit.
- */
 static int
-ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_stop_locked(struct ath5k_softc *sc)
 {
-       struct sk_buff *skb = bf->skb;
-       struct  ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ath5k_hw *ah = sc->ah;
-       struct ath5k_desc *ds;
-       int ret = 0;
-       u8 antenna;
-       u32 flags;
-       const int padsize = 0;
-
-       bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
-                       PCI_DMA_TODEVICE);
-       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] "
-                       "skbaddr %llx\n", skb, skb->data, skb->len,
-                       (unsigned long long)bf->skbaddr);
-       if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) {
-               ATH5K_ERR(sc, "beacon DMA mapping failed\n");
-               return -EIO;
-       }
-
-       ds = bf->desc;
-       antenna = ah->ah_tx_ant;
 
-       flags = AR5K_TXDESC_NOACK;
-       if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
-               ds->ds_link = bf->daddr;        /* self-linked */
-               flags |= AR5K_TXDESC_VEOL;
-       } else
-               ds->ds_link = 0;
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
+                       test_bit(ATH_STAT_INVALID, sc->status));
 
        /*
-        * If we use multiple antennas on AP and use
-        * the Sectored AP scenario, switch antenna every
-        * 4 beacons to make sure everybody hears our AP.
-        * When a client tries to associate, hw will keep
-        * track of the tx antenna to be used for this client
-        * automaticaly, based on ACKed packets.
-        *
-        * Note: AP still listens and transmits RTS on the
-        * default antenna which is supposed to be an omni.
+        * 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
         *
-        * Note2: On sectored scenarios it's possible to have
-        * multiple antennas (1 omni -- the default -- and 14
-        * sectors), so if we choose to actually support this
-        * mode, we need to allow the user to set how many antennas
-        * we have and tweak the code below to send beacons
-        * on all of them.
+        * Note that some of this work is not possible if the
+        * hardware is gone (invalid).
         */
-       if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
-               antenna = sc->bsent & 4 ? 2 : 1;
-
+       ieee80211_stop_queues(sc->hw);
 
-       /* FIXME: If we are in g mode and rate is a CCK rate
-        * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
-        * from tx power (value is in dB units already) */
-       ds->ds_data = bf->skbaddr;
-       ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
-                       ieee80211_get_hdrlen_from_skb(skb), padsize,
-                       AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
-                       ieee80211_get_tx_rate(sc->hw, info)->hw_value,
-                       1, AR5K_TXKEYIX_INVALID,
-                       antenna, flags, 0, 0);
-       if (ret)
-               goto err_unmap;
+       if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+               ath5k_led_off(sc);
+               ath5k_hw_set_imr(ah, 0);
+               synchronize_irq(sc->pdev->irq);
+       }
+       ath5k_txq_cleanup(sc);
+       if (!test_bit(ATH_STAT_INVALID, sc->status)) {
+               ath5k_rx_stop(sc);
+               ath5k_hw_phy_disable(ah);
+       }
 
        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 called from software irq context (beacontq tasklets)
- * or user context from ath5k_beacon_config.
- */
-static void
-ath5k_beacon_send(struct ath5k_softc *sc)
+static int
+ath5k_init(struct ath5k_softc *sc)
 {
-       struct ath5k_buf *bf = sc->bbuf;
        struct ath5k_hw *ah = sc->ah;
-       struct sk_buff *skb;
+       struct ath_common *common = ath5k_hw_common(ah);
+       int ret, i;
 
-       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
+       mutex_lock(&sc->lock);
+
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
 
-       if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION)) {
-               ATH5K_WARN(sc, "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.
+        * Stop anything previously setup.  This is safe
+        * no matter this is the first time through or not.
         */
-       if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) {
-               sc->bmisscount++;
-               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-                       "missed %u consecutive beacons\n", sc->bmisscount);
-               if (sc->bmisscount > 10) {      /* NB: 10 is a guess */
-                       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-                               "stuck beacon time (%u missed)\n",
-                               sc->bmisscount);
-                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                                 "stuck beacon, resetting\n");
-                       ieee80211_queue_work(sc->hw, &sc->reset_work);
-               }
-               return;
-       }
-       if (unlikely(sc->bmisscount != 0)) {
-               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-                       "resume beacon xmit after %u misses\n",
-                       sc->bmisscount);
-               sc->bmisscount = 0;
-       }
+       ath5k_stop_locked(sc);
 
        /*
-        * 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.
+        * 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.
         */
-       if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) {
-               ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq);
-               /* NB: hw still stops DMA, so proceed */
+       sc->curchan = sc->hw->conf.channel;
+       sc->curband = &sc->sbands[sc->curchan->band];
+       sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+               AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+               AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+
+       ret = ath5k_reset(sc, NULL);
+       if (ret)
+               goto done;
+
+       ath5k_rfkill_hw_start(ah);
+
+       /*
+        * Reset the key cache since some parts do not reset the
+        * contents on initial power up or resume from suspend.
+        */
+       for (i = 0; i < common->keymax; i++)
+               ath_hw_keyreset(common, (u16) i);
+
+       ath5k_hw_set_ack_bitrate_high(ah, true);
+       ret = 0;
+done:
+       mmiowb();
+       mutex_unlock(&sc->lock);
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+                       msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
+
+       return ret;
+}
+
+static void stop_tasklets(struct ath5k_softc *sc)
+{
+       tasklet_kill(&sc->rxtq);
+       tasklet_kill(&sc->txtq);
+       tasklet_kill(&sc->calib);
+       tasklet_kill(&sc->beacontq);
+       tasklet_kill(&sc->ani_tasklet);
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath5k_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+static int
+ath5k_stop_hw(struct ath5k_softc *sc)
+{
+       int ret;
+
+       mutex_lock(&sc->lock);
+       ret = ath5k_stop_locked(sc);
+       if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
+               /*
+                * Don't set the card in full sleep mode!
+                *
+                * a) When the device 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
+                * (madwifi sources mentioned srev >= 0x78) that go to
+                * sleep more quickly.
+                *
+                * b) On older chips full sleep results a weird behaviour
+                * during wakeup. I tested various cards with srev < 0x78
+                * and they don't wake up after module reload, a second
+                * module reload is needed to bring the card up again.
+                *
+                * Until we figure out what's going on don't enable
+                * full chip reset on any chip (this is what Legacy HAL
+                * and Sam's HAL do anyway). Instead Perform a full reset
+                * on the device (same as initial state after attach) and
+                * leave it idle (keep MAC/BB on warm reset) */
+               ret = ath5k_hw_on_hold(sc->ah);
+
+               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                               "putting device to sleep\n");
        }
+       ath5k_txbuf_free_skb(sc, sc->bbuf);
 
-       /* refresh the beacon for AP mode */
-       if (sc->opmode == NL80211_IFTYPE_AP)
-               ath5k_beacon_update(sc->hw, sc->vif);
+       mmiowb();
+       mutex_unlock(&sc->lock);
 
-       ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
-       ath5k_hw_start_tx_dma(ah, sc->bhalq);
-       ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
-               sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
+       stop_tasklets(sc);
 
-       skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
-       while (skb) {
-               ath5k_tx_queue(sc->hw, skb, sc->cabq);
-               skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
-       }
+       cancel_delayed_work_sync(&sc->tx_complete_work);
 
-       sc->bsent++;
-}
+       ath5k_rfkill_hw_stop(sc->ah);
 
+       return ret;
+}
 
-/**
- * ath5k_beacon_update_timers - update beacon timers
- *
- * @sc: struct ath5k_softc pointer we are operating on
- * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a
- *          beacon timer update based on the current HW TSF.
- *
- * Calculate the next target beacon transmit time (TBTT) based on the timestamp
- * of a received beacon or the current local hardware TSF and write it to the
- * beacon timer registers.
+/*
+ * Reset the hardware.  If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
  *
- * This is called in a variety of situations, e.g. when a beacon is received,
- * when a TSF update has been detected, but also when an new IBSS is created or
- * when we otherwise know we have to update the timers, but we keep it in this
- * function to have it all together in one place.
+ * This should be called with sc->lock.
  */
-static void
-ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
+static int
+ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
 {
        struct ath5k_hw *ah = sc->ah;
-       u32 nexttbtt, intval, hw_tu, bc_tu;
-       u64 hw_tsf;
+       int ret;
 
-       intval = sc->bintval & AR5K_BEACON_PERIOD;
-       if (WARN_ON(!intval))
-               return;
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
 
-       /* beacon TSF converted to TU */
-       bc_tu = TSF_TO_TU(bc_tsf);
+       ath5k_hw_set_imr(ah, 0);
+       synchronize_irq(sc->pdev->irq);
+       stop_tasklets(sc);
 
-       /* current TSF converted to TU */
-       hw_tsf = ath5k_hw_get_tsf64(ah);
-       hw_tu = TSF_TO_TU(hw_tsf);
+       if (chan) {
+               ath5k_txq_cleanup(sc);
+               ath5k_rx_stop(sc);
 
-#define FUDGE 3
-       /* we use FUDGE to make sure the next TBTT is ahead of the current TU */
-       if (bc_tsf == -1) {
-               /*
-                * no beacons received, called internally.
-                * just need to refresh timers based on HW TSF.
-                */
-               nexttbtt = roundup(hw_tu + FUDGE, intval);
-       } else if (bc_tsf == 0) {
-               /*
-                * no beacon received, probably called by ath5k_reset_tsf().
-                * reset TSF to start with 0.
-                */
-               nexttbtt = intval;
-               intval |= AR5K_BEACON_RESET_TSF;
-       } else if (bc_tsf > hw_tsf) {
-               /*
-                * beacon received, SW merge happend but HW TSF not yet updated.
-                * not possible to reconfigure timers yet, but next time we
-                * receive a beacon with the same BSSID, the hardware will
-                * automatically update the TSF and then we need to reconfigure
-                * the timers.
-                */
-               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                       "need to wait for HW TSF sync\n");
-               return;
-       } else {
-               /*
-                * most important case for beacon synchronization between STA.
-                *
-                * beacon received and HW TSF has been already updated by HW.
-                * update next TBTT based on the TSF of the beacon, but make
-                * sure it is ahead of our local TSF timer.
-                */
-               nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval);
+               sc->curchan = chan;
+               sc->curband = &sc->sbands[chan->band];
+       }
+       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
+       if (ret) {
+               ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
+               goto err;
        }
-#undef FUDGE
 
-       sc->nexttbtt = nexttbtt;
+       ret = ath5k_rx_start(sc);
+       if (ret) {
+               ATH5K_ERR(sc, "can't start recv logic\n");
+               goto err;
+       }
 
-       intval |= AR5K_BEACON_ENA;
-       ath5k_hw_init_beacon(ah, nexttbtt, intval);
+       ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
+
+       ah->ah_cal_next_full = jiffies;
+       ah->ah_cal_next_ani = jiffies;
+       ah->ah_cal_next_nf = jiffies;
 
        /*
-        * debugging output last in order to preserve the time critical aspect
-        * of this function
+        * Change channels and update the h/w rate map if we're switching;
+        * e.g. 11a to 11b/g.
+        *
+        * 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?
         */
-       if (bc_tsf == -1)
-               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                       "reconfigured timers based on HW TSF\n");
-       else if (bc_tsf == 0)
-               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                       "reset HW TSF and timers\n");
-       else
-               ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                       "updated timers based on beacon TSF\n");
+/*     ath5k_chan_change(sc, c); */
 
-       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-                         "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n",
-                         (unsigned long long) bc_tsf,
-                         (unsigned long long) hw_tsf, bc_tu, hw_tu, nexttbtt);
-       ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "intval %u %s %s\n",
-               intval & AR5K_BEACON_PERIOD,
-               intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "",
-               intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
+       ath5k_beacon_config(sc);
+       /* intrs are enabled by ath5k_beacon_config */
+
+       ieee80211_wake_queues(sc->hw);
+
+       return 0;
+err:
+       return ret;
 }
 
+static void ath5k_reset_work(struct work_struct *work)
+{
+       struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+               reset_work);
 
-/**
- * ath5k_beacon_config - Configure the beacon queues and interrupts
- *
- * @sc: struct ath5k_softc pointer we are operating on
- *
- * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
- * interrupts to detect TSF updates only.
- */
-static void
-ath5k_beacon_config(struct ath5k_softc *sc)
+       mutex_lock(&sc->lock);
+       ath5k_reset(sc, sc->curchan);
+       mutex_unlock(&sc->lock);
+}
+
+static int
+ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 {
+       struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       unsigned long flags;
+       struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+       struct ath5k_txq *txq;
+       u8 mac[ETH_ALEN] = {};
+       int ret;
 
-       spin_lock_irqsave(&sc->block, flags);
-       sc->bmisscount = 0;
-       sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
+       ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
 
-       if (sc->enable_beacon) {
-               /*
-                * In IBSS mode we use a self-linked tx descriptor and let the
-                * hardware send the beacons automatically. We have to load it
-                * only once here.
-                * We use the SWBA interrupt only to keep track of the beacon
-                * timers in order to detect automatic TSF updates.
-                */
-               ath5k_beaconq_config(sc);
+       /*
+        * Check if the MAC has multi-rate retry support.
+        * We do this by trying to setup a fake extended
+        * descriptor.  MACs that don't have support will
+        * return false w/o doing anything.  MACs that do
+        * support it will return true w/o doing anything.
+        */
+       ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
+
+       if (ret < 0)
+               goto err;
+       if (ret > 0)
+               __set_bit(ATH_STAT_MRRETRY, sc->status);
+
+       /*
+        * Collect the channel list.  The 802.11 layer
+        * is resposible for filtering this list based
+        * on settings like the phy mode and regulatory
+        * domain restrictions.
+        */
+       ret = ath5k_setup_bands(hw);
+       if (ret) {
+               ATH5K_ERR(sc, "can't get channels\n");
+               goto err;
+       }
+
+       /* NB: setup here so ath5k_rate_update is happy */
+       if (test_bit(AR5K_MODE_11A, ah->ah_modes))
+               ath5k_setcurmode(sc, AR5K_MODE_11A);
+       else
+               ath5k_setcurmode(sc, AR5K_MODE_11B);
+
+       /*
+        * Allocate tx+rx descriptors and populate the lists.
+        */
+       ret = ath5k_desc_alloc(sc, pdev);
+       if (ret) {
+               ATH5K_ERR(sc, "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 hw functions handle resetting
+        * these queues at the needed time.
+        */
+       ret = ath5k_beaconq_setup(ah);
+       if (ret < 0) {
+               ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
+               goto err_desc;
+       }
+       sc->bhalq = ret;
+       sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
+       if (IS_ERR(sc->cabq)) {
+               ATH5K_ERR(sc, "can't setup cab queue\n");
+               ret = PTR_ERR(sc->cabq);
+               goto err_bhal;
+       }
+
+       /* This order matches mac80211's queue priority, so we can
+        * directly use the mac80211 queue number without any mapping */
+       txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VO);
+       if (IS_ERR(txq)) {
+               ATH5K_ERR(sc, "can't setup xmit queue\n");
+               ret = PTR_ERR(txq);
+               goto err_queues;
+       }
+       txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_VI);
+       if (IS_ERR(txq)) {
+               ATH5K_ERR(sc, "can't setup xmit queue\n");
+               ret = PTR_ERR(txq);
+               goto err_queues;
+       }
+       txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE);
+       if (IS_ERR(txq)) {
+               ATH5K_ERR(sc, "can't setup xmit queue\n");
+               ret = PTR_ERR(txq);
+               goto err_queues;
+       }
+       txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+       if (IS_ERR(txq)) {
+               ATH5K_ERR(sc, "can't setup xmit queue\n");
+               ret = PTR_ERR(txq);
+               goto err_queues;
+       }
+       hw->queues = 4;
 
-               sc->imask |= AR5K_INT_SWBA;
+       tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
+       tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
+       tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
+       tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
+       tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
 
-               if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-                       if (ath5k_hw_hasveol(ah))
-                               ath5k_beacon_send(sc);
-               } else
-                       ath5k_beacon_update_timers(sc, -1);
-       } else {
-               ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq);
-       }
+       INIT_WORK(&sc->reset_work, ath5k_reset_work);
+       INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work);
 
-       ath5k_hw_set_imr(ah, sc->imask);
-       mmiowb();
-       spin_unlock_irqrestore(&sc->block, flags);
-}
+       ret = ath5k_eeprom_read_mac(ah, mac);
+       if (ret) {
+               ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
+                       sc->pdev->device);
+               goto err_queues;
+       }
 
-static void ath5k_tasklet_beacon(unsigned long data)
-{
-       struct ath5k_softc *sc = (struct ath5k_softc *) data;
+       SET_IEEE80211_PERM_ADDR(hw, mac);
+       /* All MAC address bits matter for ACKs */
+       memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
+       ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
 
-       /*
-        * Software beacon alert--time to send a beacon.
-        *
-        * In IBSS mode we use this interrupt just to
-        * keep track of the next TBTT (target beacon
-        * transmission time) in order to detect wether
-        * automatic TSF updates happened.
-        */
-       if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-               /* XXX: only if VEOL suppported */
-               u64 tsf = ath5k_hw_get_tsf64(sc->ah);
-               sc->nexttbtt += sc->bintval;
-               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-                               "SWBA nexttbtt: %x hw_tu: %x "
-                               "TSF: %llx\n",
-                               sc->nexttbtt,
-                               TSF_TO_TU(tsf),
-                               (unsigned long long) tsf);
-       } else {
-               spin_lock(&sc->block);
-               ath5k_beacon_send(sc);
-               spin_unlock(&sc->block);
+       regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
+       ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
+       if (ret) {
+               ATH5K_ERR(sc, "can't initialize regulatory system\n");
+               goto err_queues;
        }
-}
 
+       ret = ieee80211_register_hw(hw);
+       if (ret) {
+               ATH5K_ERR(sc, "can't register ieee80211 hw\n");
+               goto err_queues;
+       }
 
-/********************\
-* Interrupt handling *
-\********************/
+       if (!ath_is_world_regd(regulatory))
+               regulatory_hint(hw->wiphy, regulatory->alpha2);
 
-static int
-ath5k_init(struct ath5k_softc *sc)
-{
-       struct ath5k_hw *ah = sc->ah;
-       int ret, i;
+       ath5k_init_leds(sc);
 
-       mutex_lock(&sc->lock);
+       ath5k_sysfs_register(sc);
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
+       return 0;
+err_queues:
+       ath5k_txq_release(sc);
+err_bhal:
+       ath5k_hw_release_tx_queue(ah, sc->bhalq);
+err_desc:
+       ath5k_desc_free(sc, pdev);
+err:
+       return ret;
+}
 
-       /*
-        * Stop anything previously setup.  This is safe
-        * no matter this is the first time through or not.
-        */
-       ath5k_stop_locked(sc);
+static void
+ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+{
+       struct ath5k_softc *sc = hw->priv;
 
        /*
-        * 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.
+        * NB: the order of these is important:
+        * o call the 802.11 layer before detaching ath5k_hw to
+        *   ensure 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
+        * XXX: ??? detach ath5k_hw ???
+        * Other than that, it's straightforward...
         */
-       sc->curchan = sc->hw->conf.channel;
-       sc->curband = &sc->sbands[sc->curchan->band];
-       sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
-               AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
-               AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
-
-       ret = ath5k_reset(sc, NULL);
-       if (ret)
-               goto done;
-
-       ath5k_rfkill_hw_start(ah);
+       ieee80211_unregister_hw(hw);
+       ath5k_desc_free(sc, pdev);
+       ath5k_txq_release(sc);
+       ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
+       ath5k_unregister_leds(sc);
 
+       ath5k_sysfs_unregister(sc);
        /*
-        * Reset the key cache since some parts do not reset the
-        * contents on initial power up or resume from suspend.
+        * 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.
         */
-       for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
-               ath5k_hw_reset_key(ah, i);
-
-       ath5k_hw_set_ack_bitrate_high(ah, true);
-       ret = 0;
-done:
-       mmiowb();
-       mutex_unlock(&sc->lock);
-       return ret;
 }
 
+/********************\
+* Mac80211 functions *
+\********************/
+
 static int
-ath5k_stop_locked(struct ath5k_softc *sc)
+ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
-       struct ath5k_hw *ah = sc->ah;
-
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
-                       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);
+       struct ath5k_softc *sc = hw->priv;
+       u16 qnum = skb_get_queue_mapping(skb);
 
-       if (!test_bit(ATH_STAT_INVALID, sc->status)) {
-               ath5k_led_off(sc);
-               ath5k_hw_set_imr(ah, 0);
-               synchronize_irq(sc->pdev->irq);
-       }
-       ath5k_txq_cleanup(sc);
-       if (!test_bit(ATH_STAT_INVALID, sc->status)) {
-               ath5k_rx_stop(sc);
-               ath5k_hw_phy_disable(ah);
+       if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) {
+               dev_kfree_skb_any(skb);
+               return 0;
        }
 
-       return 0;
+       return ath5k_tx_queue(hw, skb, &sc->txqs[qnum]);
 }
 
-static void stop_tasklets(struct ath5k_softc *sc)
+static int ath5k_start(struct ieee80211_hw *hw)
 {
-       tasklet_kill(&sc->rxtq);
-       tasklet_kill(&sc->txtq);
-       tasklet_kill(&sc->calib);
-       tasklet_kill(&sc->beacontq);
-       tasklet_kill(&sc->ani_tasklet);
+       return ath5k_init(hw->priv);
 }
 
-/*
- * Stop the device, grabbing the top-level lock to protect
- * against concurrent entry through ath5k_init (which can happen
- * if another thread does a system call and the thread doing the
- * stop is preempted).
- */
-static int
-ath5k_stop_hw(struct ath5k_softc *sc)
+static void ath5k_stop(struct ieee80211_hw *hw)
+{
+       ath5k_stop_hw(hw->priv);
+}
+
+static int ath5k_add_interface(struct ieee80211_hw *hw,
+               struct ieee80211_vif *vif)
 {
+       struct ath5k_softc *sc = hw->priv;
        int ret;
 
        mutex_lock(&sc->lock);
-       ret = ath5k_stop_locked(sc);
-       if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
-               /*
-                * Don't set the card in full sleep mode!
-                *
-                * a) When the device 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
-                * (madwifi sources mentioned srev >= 0x78) that go to
-                * sleep more quickly.
-                *
-                * b) On older chips full sleep results a weird behaviour
-                * during wakeup. I tested various cards with srev < 0x78
-                * and they don't wake up after module reload, a second
-                * module reload is needed to bring the card up again.
-                *
-                * Until we figure out what's going on don't enable
-                * full chip reset on any chip (this is what Legacy HAL
-                * and Sam's HAL do anyway). Instead Perform a full reset
-                * on the device (same as initial state after attach) and
-                * leave it idle (keep MAC/BB on warm reset) */
-               ret = ath5k_hw_on_hold(sc->ah);
-
-               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                               "putting device to sleep\n");
+       if (sc->vif) {
+               ret = 0;
+               goto end;
        }
-       ath5k_txbuf_free_skb(sc, sc->bbuf);
 
-       mmiowb();
-       mutex_unlock(&sc->lock);
+       sc->vif = vif;
 
-       stop_tasklets(sc);
+       switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+               sc->opmode = vif->type;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               goto end;
+       }
+
+       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode);
 
-       ath5k_rfkill_hw_stop(sc->ah);
+       ath5k_hw_set_lladdr(sc->ah, vif->addr);
+       ath5k_mode_setup(sc);
 
+       ret = 0;
+end:
+       mutex_unlock(&sc->lock);
        return ret;
 }
 
 static void
-ath5k_intr_calibration_poll(struct ath5k_hw *ah)
+ath5k_remove_interface(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif)
 {
-       if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
-           !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
-               /* run ANI only when full calibration is not active */
-               ah->ah_cal_next_ani = jiffies +
-                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
-               tasklet_schedule(&ah->ah_sc->ani_tasklet);
+       struct ath5k_softc *sc = hw->priv;
+       u8 mac[ETH_ALEN] = {};
 
-       } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
-               ah->ah_cal_next_full = jiffies +
-                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
-               tasklet_schedule(&ah->ah_sc->calib);
-       }
-       /* we could use SWI to generate enough interrupts to meet our
-        * calibration interval requirements, if necessary:
-        * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
+       mutex_lock(&sc->lock);
+       if (sc->vif != vif)
+               goto end;
+
+       ath5k_hw_set_lladdr(sc->ah, mac);
+       sc->vif = NULL;
+end:
+       mutex_unlock(&sc->lock);
 }
 
-static irqreturn_t
-ath5k_intr(int irq, void *dev_id)
+/*
+ * TODO: Phy disable/diversity etc
+ */
+static int
+ath5k_config(struct ieee80211_hw *hw, u32 changed)
 {
-       struct ath5k_softc *sc = dev_id;
+       struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       enum ath5k_int status;
-       unsigned int counter = 1000;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
 
-       if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
-                               !ath5k_hw_is_intr_pending(ah)))
-               return IRQ_NONE;
+       mutex_lock(&sc->lock);
 
-       do {
-               ath5k_hw_get_isr(ah, &status);          /* NB: clears IRQ too */
-               ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
-                               status, sc->imask);
-               if (unlikely(status & AR5K_INT_FATAL)) {
-                       /*
-                        * Fatal errors are unrecoverable.
-                        * Typically these are caused by DMA errors.
-                        */
-                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                                 "fatal int, resetting\n");
-                       ieee80211_queue_work(sc->hw, &sc->reset_work);
-               } else if (unlikely(status & AR5K_INT_RXORN)) {
-                       /*
-                        * Receive buffers are full. Either the bus is busy or
-                        * the CPU is not fast enough to process all received
-                        * frames.
-                        * Older chipsets need a reset to come out of this
-                        * condition, but we treat it as RX for newer chips.
-                        * We don't know exactly which versions need a reset -
-                        * this guess is copied from the HAL.
-                        */
-                       sc->stats.rxorn_intr++;
-                       if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
-                               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                                         "rx overrun, resetting\n");
-                               ieee80211_queue_work(sc->hw, &sc->reset_work);
-                       }
-                       else
-                               tasklet_schedule(&sc->rxtq);
-               } else {
-                       if (status & AR5K_INT_SWBA) {
-                               tasklet_hi_schedule(&sc->beacontq);
-                       }
-                       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->stats.rxeol_intr++;
-                       }
-                       if (status & AR5K_INT_TXURN) {
-                               /* bump tx trigger level */
-                               ath5k_hw_update_tx_triglevel(ah, true);
-                       }
-                       if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
-                               tasklet_schedule(&sc->rxtq);
-                       if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
-                                       | AR5K_INT_TXERR | AR5K_INT_TXEOL))
-                               tasklet_schedule(&sc->txtq);
-                       if (status & AR5K_INT_BMISS) {
-                               /* TODO */
-                       }
-                       if (status & AR5K_INT_MIB) {
-                               sc->stats.mib_intr++;
-                               ath5k_hw_update_mib_counters(ah);
-                               ath5k_ani_mib_intr(ah);
-                       }
-                       if (status & AR5K_INT_GPIO)
-                               tasklet_schedule(&sc->rf_kill.toggleq);
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ret = ath5k_chan_set(sc, conf->channel);
+               if (ret < 0)
+                       goto unlock;
+       }
 
-               }
-       } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
+       if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
+       (sc->power_level != conf->power_level)) {
+               sc->power_level = conf->power_level;
 
-       if (unlikely(!counter))
-               ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
+               /* Half dB steps */
+               ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
+       }
 
-       ath5k_intr_calibration_poll(ah);
+       /* TODO:
+        * 1) Move this on config_interface and handle each case
+        * separately eg. when we have only one STA vif, use
+        * AR5K_ANTMODE_SINGLE_AP
+        *
+        * 2) Allow the user to change antenna mode eg. when only
+        * one antenna is present
+        *
+        * 3) Allow the user to set default/tx antenna when possible
+        *
+        * 4) Default mode should handle 90% of the cases, together
+        * with fixed a/b and single AP modes we should be able to
+        * handle 99%. Sectored modes are extreme cases and i still
+        * haven't found a usage for them. If we decide to support them,
+        * then we must allow the user to set how many tx antennas we
+        * have available
+        */
+       ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
 
-       return IRQ_HANDLED;
+unlock:
+       mutex_unlock(&sc->lock);
+       return ret;
+}
+
+static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
+                                  struct netdev_hw_addr_list *mc_list)
+{
+       u32 mfilt[2], val;
+       u8 pos;
+       struct netdev_hw_addr *ha;
+
+       mfilt[0] = 0;
+       mfilt[1] = 1;
+
+       netdev_hw_addr_list_for_each(ha, mc_list) {
+               /* calculate XOR of eight 6-bit values */
+               val = get_unaligned_le32(ha->addr + 0);
+               pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+               val = get_unaligned_le32(ha->addr + 3);
+               pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+               pos &= 0x3f;
+               mfilt[pos / 32] |= (1 << (pos % 32));
+               /* XXX: we might be able to just do this instead,
+               * but not sure, needs testing, if we do use this we'd
+               * neet to inform below to not reset the mcast */
+               /* ath5k_hw_set_mcast_filterindex(ah,
+                *      ha->addr[5]); */
+       }
+
+       return ((u64)(mfilt[1]) << 32) | mfilt[0];
 }
 
+#define SUPPORTED_FIF_FLAGS \
+       FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
+       FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
+       FIF_BCN_PRBRESP_PROMISC
 /*
- * Periodically recalibrate the PHY to account
- * for temperature/environment changes.
+ * o always accept unicast, broadcast, and multicast traffic
+ * o multicast traffic for all BSSIDs will be enabled if mac80211
+ *   says it should be
+ * o maintain current state of phy ofdm or phy cck error reception.
+ *   If the hardware detects any of these type of errors then
+ *   ath5k_hw_get_rx_filter() will pass to us the respective
+ *   hardware filters to be able to receive these type of frames.
+ * 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
-ath5k_tasklet_calibrate(unsigned long data)
+static void ath5k_configure_filter(struct ieee80211_hw *hw,
+               unsigned int changed_flags,
+               unsigned int *new_flags,
+               u64 multicast)
 {
-       struct ath5k_softc *sc = (void *)data;
+       struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
+       u32 mfilt[2], rfilt;
 
-       /* Only full calibration for now */
-       ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
+       mutex_lock(&sc->lock);
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
-               ieee80211_frequency_to_channel(sc->curchan->center_freq),
-               sc->curchan->hw_value);
+       mfilt[0] = multicast;
+       mfilt[1] = multicast >> 32;
 
-       if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
-               /*
-                * Rfgain is out of bounds, reset the chip
-                * to load new gain values.
-                */
-               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
-               ieee80211_queue_work(sc->hw, &sc->reset_work);
+       /* Only deal with supported flags */
+       changed_flags &= SUPPORTED_FIF_FLAGS;
+       *new_flags &= SUPPORTED_FIF_FLAGS;
+
+       /* If HW detects any phy or radar errors, leave those filters on.
+        * Also, always enable Unicast, Broadcasts and Multicast
+        * XXX: move unicast, bssid broadcasts and multicast to mac80211 */
+       rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) |
+               (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
+               AR5K_RX_FILTER_MCAST);
+
+       if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) {
+               if (*new_flags & FIF_PROMISC_IN_BSS) {
+                       __set_bit(ATH_STAT_PROMISC, sc->status);
+               } else {
+                       __clear_bit(ATH_STAT_PROMISC, sc->status);
+               }
        }
-       if (ath5k_hw_phy_calibrate(ah, sc->curchan))
-               ATH5K_ERR(sc, "calibration of channel %u failed\n",
-                       ieee80211_frequency_to_channel(
-                               sc->curchan->center_freq));
 
-       /* Noise floor calibration interrupts rx/tx path while I/Q calibration
-        * doesn't. We stop the queues so that calibration doesn't interfere
-        * with TX and don't run it as often */
-       if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
-               ah->ah_cal_next_nf = jiffies +
-                       msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
-               ieee80211_stop_queues(sc->hw);
-               ath5k_hw_update_noise_floor(ah);
-               ieee80211_wake_queues(sc->hw);
+       if (test_bit(ATH_STAT_PROMISC, sc->status))
+               rfilt |= AR5K_RX_FILTER_PROM;
+
+       /* Note, AR5K_RX_FILTER_MCAST is already enabled */
+       if (*new_flags & FIF_ALLMULTI) {
+               mfilt[0] =  ~0;
+               mfilt[1] =  ~0;
        }
 
-       ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
-}
+       /* 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 */
+       if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
+               rfilt |= AR5K_RX_FILTER_BEACON;
 
-static void
-ath5k_tasklet_ani(unsigned long data)
-{
-       struct ath5k_softc *sc = (void *)data;
-       struct ath5k_hw *ah = sc->ah;
+       /* FIF_CONTROL doc says that if 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, which is OK.. but
+        * but we should see if we can improve on granularity */
+       if (*new_flags & FIF_CONTROL)
+               rfilt |= AR5K_RX_FILTER_CONTROL;
 
-       ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
-       ath5k_ani_calibration(ah);
-       ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
-}
+       /* Additional settings per mode -- this is per ath5k */
 
+       /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
 
-/********************\
-* Mac80211 functions *
-\********************/
+       switch (sc->opmode) {
+       case NL80211_IFTYPE_MESH_POINT:
+               rfilt |= AR5K_RX_FILTER_CONTROL |
+                        AR5K_RX_FILTER_BEACON |
+                        AR5K_RX_FILTER_PROBEREQ |
+                        AR5K_RX_FILTER_PROM;
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+               rfilt |= AR5K_RX_FILTER_PROBEREQ |
+                        AR5K_RX_FILTER_BEACON;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (sc->assoc)
+                       rfilt |= AR5K_RX_FILTER_BEACON;
+       default:
+               break;
+       }
 
-static int
-ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
-       struct ath5k_softc *sc = hw->priv;
+       /* Set filters */
+       ath5k_hw_set_rx_filter(ah, rfilt);
+
+       /* Set multicast bits */
+       ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
+       /* Set the cached hw filter flags, this will later actually
+        * be set in HW */
+       sc->filter_flags = rfilt;
 
-       return ath5k_tx_queue(hw, skb, sc->txq);
+       mutex_unlock(&sc->lock);
 }
 
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-                         struct ath5k_txq *txq)
+static int
+ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+             struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+             struct ieee80211_key_conf *key)
 {
        struct ath5k_softc *sc = hw->priv;
-       struct ath5k_buf *bf;
-       unsigned long flags;
-       int padsize;
-
-       ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
+       int ret = 0;
 
-       /*
-        * The hardware expects the header padded to 4 byte boundaries.
-        * If this is not the case, we add the padding after the header.
-        */
-       padsize = ath5k_add_padding(skb);
-       if (padsize < 0) {
-               ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
-                         " headroom to pad");
-               goto drop_packet;
-       }
+       if (modparam_nohwcrypt)
+               return -EOPNOTSUPP;
 
-       spin_lock_irqsave(&sc->txbuflock, flags);
-       if (list_empty(&sc->txbuf)) {
-               ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
-               spin_unlock_irqrestore(&sc->txbuflock, flags);
-               ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
-               goto drop_packet;
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+       case WLAN_CIPHER_SUITE_TKIP:
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               if (common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)
+                       break;
+               return -EOPNOTSUPP;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
        }
-       bf = list_first_entry(&sc->txbuf, struct ath5k_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;
+       mutex_lock(&sc->lock);
 
-       if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
-               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);
-               goto drop_packet;
+       switch (cmd) {
+       case SET_KEY:
+               ret = ath_key_config(common, vif, sta, key);
+               if (ret >= 0) {
+                       key->hw_key_idx = ret;
+                       /* push IV and Michael MIC generation to stack */
+                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+                       if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+                               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+                       if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+                               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
+                       ret = 0;
+               }
+               break;
+       case DISABLE_KEY:
+               ath_key_delete(common, key);
+               break;
+       default:
+               ret = -EINVAL;
        }
-       return NETDEV_TX_OK;
 
-drop_packet:
-       dev_kfree_skb_any(skb);
-       return NETDEV_TX_OK;
+       mmiowb();
+       mutex_unlock(&sc->lock);
+       return ret;
 }
 
-/*
- * Reset the hardware.  If chan is not NULL, then also pause rx/tx
- * and change to the given channel.
- *
- * This should be called with sc->lock.
- */
 static int
-ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+ath5k_get_stats(struct ieee80211_hw *hw,
+               struct ieee80211_low_level_stats *stats)
 {
-       struct ath5k_hw *ah = sc->ah;
-       int ret;
-
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
-
-       ath5k_hw_set_imr(ah, 0);
-       synchronize_irq(sc->pdev->irq);
-       stop_tasklets(sc);
+       struct ath5k_softc *sc = hw->priv;
 
-       if (chan) {
-               ath5k_txq_cleanup(sc);
-               ath5k_rx_stop(sc);
+       /* Force update */
+       ath5k_hw_update_mib_counters(sc->ah);
 
-               sc->curchan = chan;
-               sc->curband = &sc->sbands[chan->band];
-       }
-       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
-       if (ret) {
-               ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
-               goto err;
-       }
+       stats->dot11ACKFailureCount = sc->stats.ack_fail;
+       stats->dot11RTSFailureCount = sc->stats.rts_fail;
+       stats->dot11RTSSuccessCount = sc->stats.rts_ok;
+       stats->dot11FCSErrorCount = sc->stats.fcs_error;
 
-       ret = ath5k_rx_start(sc);
-       if (ret) {
-               ATH5K_ERR(sc, "can't start recv logic\n");
-               goto err;
-       }
+       return 0;
+}
 
-       ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
+static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
+               struct survey_info *survey)
+{
+       struct ath5k_softc *sc = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
 
-       ah->ah_cal_next_full = jiffies;
-       ah->ah_cal_next_ani = jiffies;
-       ah->ah_cal_next_nf = jiffies;
+        if (idx != 0)
+               return -ENOENT;
 
-       /*
-        * Change channels and update the h/w rate map if we're switching;
-        * e.g. 11a to 11b/g.
-        *
-        * 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?
-        */
-/*     ath5k_chan_change(sc, c); */
+       survey->channel = conf->channel;
+       survey->filled = SURVEY_INFO_NOISE_DBM;
+       survey->noise = sc->ah->ah_noise_floor;
 
-       ath5k_beacon_config(sc);
-       /* intrs are enabled by ath5k_beacon_config */
+       return 0;
+}
 
-       ieee80211_wake_queues(sc->hw);
+static u64
+ath5k_get_tsf(struct ieee80211_hw *hw)
+{
+       struct ath5k_softc *sc = hw->priv;
 
-       return 0;
-err:
-       return ret;
+       return ath5k_hw_get_tsf64(sc->ah);
 }
 
-static void ath5k_reset_work(struct work_struct *work)
+static void
+ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
 {
-       struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
-               reset_work);
+       struct ath5k_softc *sc = hw->priv;
 
-       mutex_lock(&sc->lock);
-       ath5k_reset(sc, sc->curchan);
-       mutex_unlock(&sc->lock);
+       ath5k_hw_set_tsf64(sc->ah, tsf);
 }
 
-static int ath5k_start(struct ieee80211_hw *hw)
+static void
+ath5k_reset_tsf(struct ieee80211_hw *hw)
 {
-       return ath5k_init(hw->priv);
+       struct ath5k_softc *sc = hw->priv;
+
+       /*
+        * in IBSS mode we need to update the beacon timers too.
+        * this will also reset the TSF if we call it with 0
+        */
+       if (sc->opmode == NL80211_IFTYPE_ADHOC)
+               ath5k_beacon_update_timers(sc, 0);
+       else
+               ath5k_hw_reset_tsf(sc->ah);
 }
 
-static void ath5k_stop(struct ieee80211_hw *hw)
+static void
+set_beacon_filter(struct ieee80211_hw *hw, bool enable)
 {
-       ath5k_stop_hw(hw->priv);
+       struct ath5k_softc *sc = hw->priv;
+       struct ath5k_hw *ah = sc->ah;
+       u32 rfilt;
+       rfilt = ath5k_hw_get_rx_filter(ah);
+       if (enable)
+               rfilt |= AR5K_RX_FILTER_BEACON;
+       else
+               rfilt &= ~AR5K_RX_FILTER_BEACON;
+       ath5k_hw_set_rx_filter(ah, rfilt);
+       sc->filter_flags = rfilt;
 }
 
-static int ath5k_add_interface(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif)
+static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_bss_conf *bss_conf,
+                                   u32 changes)
 {
        struct ath5k_softc *sc = hw->priv;
-       int ret;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
+       unsigned long flags;
 
        mutex_lock(&sc->lock);
-       if (sc->vif) {
-               ret = 0;
-               goto end;
+       if (WARN_ON(sc->vif != vif))
+               goto unlock;
+
+       if (changes & BSS_CHANGED_BSSID) {
+               /* Cache for later use during resets */
+               memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+               common->curaid = 0;
+               ath5k_hw_set_bssid(ah);
+               mmiowb();
        }
 
-       sc->vif = vif;
+       if (changes & BSS_CHANGED_BEACON_INT)
+               sc->bintval = bss_conf->beacon_int;
 
-       switch (vif->type) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
-               sc->opmode = vif->type;
-               break;
-       default:
-               ret = -EOPNOTSUPP;
-               goto end;
+       if (changes & BSS_CHANGED_ASSOC) {
+               sc->assoc = bss_conf->assoc;
+               if (sc->opmode == NL80211_IFTYPE_STATION)
+                       set_beacon_filter(hw, sc->assoc);
+               ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+                       AR5K_LED_ASSOC : AR5K_LED_INIT);
+               if (bss_conf->assoc) {
+                       ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+                                 "Bss Info ASSOC %d, bssid: %pM\n",
+                                 bss_conf->aid, common->curbssid);
+                       common->curaid = bss_conf->aid;
+                       ath5k_hw_set_bssid(ah);
+                       /* Once ANI is available you would start it here */
+               }
        }
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode);
+       if (changes & BSS_CHANGED_BEACON) {
+               spin_lock_irqsave(&sc->block, flags);
+               ath5k_beacon_update(hw, vif);
+               spin_unlock_irqrestore(&sc->block, flags);
+       }
 
-       ath5k_hw_set_lladdr(sc->ah, vif->addr);
-       ath5k_mode_setup(sc);
+       if (changes & BSS_CHANGED_BEACON_ENABLED)
+               sc->enable_beacon = bss_conf->enable_beacon;
 
-       ret = 0;
-end:
+       if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
+                      BSS_CHANGED_BEACON_INT))
+               ath5k_beacon_config(sc);
+
+ unlock:
        mutex_unlock(&sc->lock);
-       return ret;
 }
 
-static void
-ath5k_remove_interface(struct ieee80211_hw *hw,
-                       struct ieee80211_vif *vif)
+static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
 {
        struct ath5k_softc *sc = hw->priv;
-       u8 mac[ETH_ALEN] = {};
+       if (!sc->assoc)
+               ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
+}
 
-       mutex_lock(&sc->lock);
-       if (sc->vif != vif)
-               goto end;
+static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+       struct ath5k_softc *sc = hw->priv;
+       ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+               AR5K_LED_ASSOC : AR5K_LED_INIT);
+}
 
-       ath5k_hw_set_lladdr(sc->ah, mac);
-       sc->vif = NULL;
-end:
+/**
+ * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
+ *
+ * @hw: struct ieee80211_hw pointer
+ * @coverage_class: IEEE 802.11 coverage class number
+ *
+ * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
+ * coverage class. The values are persistent, they are restored after device
+ * reset.
+ */
+static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
+{
+       struct ath5k_softc *sc = hw->priv;
+
+       mutex_lock(&sc->lock);
+       ath5k_hw_set_coverage_class(sc->ah, coverage_class);
        mutex_unlock(&sc->lock);
 }
 
-/*
- * TODO: Phy disable/diversity etc
- */
-static int
-ath5k_config(struct ieee80211_hw *hw, u32 changed)
+static int ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+                        const struct ieee80211_tx_queue_params *params)
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       struct ieee80211_conf *conf = &hw->conf;
+       struct ath5k_txq_info qi;
        int ret = 0;
 
+       if (queue >= ah->ah_capabilities.cap_queues.q_tx_num)
+               return 0;
+
        mutex_lock(&sc->lock);
 
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               ret = ath5k_chan_set(sc, conf->channel);
-               if (ret < 0)
-                       goto unlock;
-       }
+       ath5k_hw_get_tx_queueprops(ah, queue, &qi);
 
-       if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
-       (sc->power_level != conf->power_level)) {
-               sc->power_level = conf->power_level;
+       qi.tqi_aifs = params->aifs;
+       qi.tqi_cw_min = params->cw_min;
+       qi.tqi_cw_max = params->cw_max;
+       qi.tqi_burst_time = params->txop;
 
-               /* Half dB steps */
-               ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
-       }
+       ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+                 "Configure tx [queue %d],  "
+                 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+                 queue, params->aifs, params->cw_min,
+                 params->cw_max, params->txop);
 
-       /* TODO:
-        * 1) Move this on config_interface and handle each case
-        * separately eg. when we have only one STA vif, use
-        * AR5K_ANTMODE_SINGLE_AP
-        *
-        * 2) Allow the user to change antenna mode eg. when only
-        * one antenna is present
-        *
-        * 3) Allow the user to set default/tx antenna when possible
-        *
-        * 4) Default mode should handle 90% of the cases, together
-        * with fixed a/b and single AP modes we should be able to
-        * handle 99%. Sectored modes are extreme cases and i still
-        * haven't found a usage for them. If we decide to support them,
-        * then we must allow the user to set how many tx antennas we
-        * have available
-        */
-       ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
+       if (ath5k_hw_set_tx_queueprops(ah, queue, &qi)) {
+               ATH5K_ERR(sc,
+                         "Unable to update hardware queue %u!\n", queue);
+               ret = -EIO;
+       } else
+               ath5k_hw_reset_tx_queue(ah, queue);
 
-unlock:
        mutex_unlock(&sc->lock);
+
        return ret;
 }
 
-static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
-                                  struct netdev_hw_addr_list *mc_list)
-{
-       u32 mfilt[2], val;
-       u8 pos;
-       struct netdev_hw_addr *ha;
+static const struct ieee80211_ops ath5k_hw_ops = {
+       .tx             = ath5k_tx,
+       .start          = ath5k_start,
+       .stop           = ath5k_stop,
+       .add_interface  = ath5k_add_interface,
+       .remove_interface = ath5k_remove_interface,
+       .config         = ath5k_config,
+       .prepare_multicast = ath5k_prepare_multicast,
+       .configure_filter = ath5k_configure_filter,
+       .set_key        = ath5k_set_key,
+       .get_stats      = ath5k_get_stats,
+       .get_survey     = ath5k_get_survey,
+       .conf_tx        = ath5k_conf_tx,
+       .get_tsf        = ath5k_get_tsf,
+       .set_tsf        = ath5k_set_tsf,
+       .reset_tsf      = ath5k_reset_tsf,
+       .bss_info_changed = ath5k_bss_info_changed,
+       .sw_scan_start  = ath5k_sw_scan_start,
+       .sw_scan_complete = ath5k_sw_scan_complete,
+       .set_coverage_class = ath5k_set_coverage_class,
+};
 
-       mfilt[0] = 0;
-       mfilt[1] = 1;
+/********************\
+* PCI Initialization *
+\********************/
 
-       netdev_hw_addr_list_for_each(ha, mc_list) {
-               /* calculate XOR of eight 6-bit values */
-               val = get_unaligned_le32(ha->addr + 0);
-               pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
-               val = get_unaligned_le32(ha->addr + 3);
-               pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
-               pos &= 0x3f;
-               mfilt[pos / 32] |= (1 << (pos % 32));
-               /* XXX: we might be able to just do this instead,
-               * but not sure, needs testing, if we do use this we'd
-               * neet to inform below to not reset the mcast */
-               /* ath5k_hw_set_mcast_filterindex(ah,
-                *      ha->addr[5]); */
-       }
+static int __devinit
+ath5k_pci_probe(struct pci_dev *pdev,
+               const struct pci_device_id *id)
+{
+       void __iomem *mem;
+       struct ath5k_softc *sc;
+       struct ath_common *common;
+       struct ieee80211_hw *hw;
+       int ret;
+       u8 csz;
 
-       return ((u64)(mfilt[1]) << 32) | mfilt[0];
-}
+       /*
+        * L0s needs to be disabled on all ath5k cards.
+        *
+        * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
+        * by default in the future in 2.6.36) this will also mean both L1 and
+        * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
+        * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
+        * though but cannot currently undue the effect of a blacklist, for
+        * details you can read pcie_aspm_sanity_check() and see how it adjusts
+        * the device link capability.
+        *
+        * It may be possible in the future to implement some PCI API to allow
+        * drivers to override blacklists for pre 1.1 PCIe but for now it is
+        * best to accept that both L0s and L1 will be disabled completely for
+        * distributions shipping with CONFIG_PCIEASPM rather than having this
+        * issue present. Motivation for adding this new API will be to help
+        * with power consumption for some of these devices.
+        */
+       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
 
-#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 multicast traffic for all BSSIDs will be enabled if mac80211
- *   says it should be
- * o maintain current state of phy ofdm or phy cck error reception.
- *   If the hardware detects any of these type of errors then
- *   ath5k_hw_get_rx_filter() will pass to us the respective
- *   hardware filters to be able to receive these type of frames.
- * 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 ath5k_configure_filter(struct ieee80211_hw *hw,
-               unsigned int changed_flags,
-               unsigned int *new_flags,
-               u64 multicast)
-{
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       u32 mfilt[2], rfilt;
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "can't enable device\n");
+               goto err;
+       }
 
-       mutex_lock(&sc->lock);
+       /* XXX 32-bit addressing only */
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_err(&pdev->dev, "32-bit DMA not available\n");
+               goto err_dis;
+       }
 
-       mfilt[0] = multicast;
-       mfilt[1] = multicast >> 32;
+       /*
+        * 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 >> 2;
+               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);
 
-       /* Only deal with supported flags */
-       changed_flags &= SUPPORTED_FIF_FLAGS;
-       *new_flags &= SUPPORTED_FIF_FLAGS;
+       /* Enable bus mastering */
+       pci_set_master(pdev);
 
-       /* If HW detects any phy or radar errors, leave those filters on.
-        * Also, always enable Unicast, Broadcasts and Multicast
-        * XXX: move unicast, bssid broadcasts and multicast to mac80211 */
-       rfilt = (ath5k_hw_get_rx_filter(ah) & (AR5K_RX_FILTER_PHYERR)) |
-               (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST |
-               AR5K_RX_FILTER_MCAST);
+       /*
+        * Disable the RETRY_TIMEOUT register (0x41) to keep
+        * PCI Tx retries from interfering with C3 CPU state.
+        */
+       pci_write_config_byte(pdev, 0x41, 0);
 
-       if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) {
-               if (*new_flags & FIF_PROMISC_IN_BSS) {
-                       __set_bit(ATH_STAT_PROMISC, sc->status);
-               } else {
-                       __clear_bit(ATH_STAT_PROMISC, sc->status);
-               }
+       ret = pci_request_region(pdev, 0, "ath5k");
+       if (ret) {
+               dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+               goto err_dis;
        }
 
-       if (test_bit(ATH_STAT_PROMISC, sc->status))
-               rfilt |= AR5K_RX_FILTER_PROM;
-
-       /* Note, AR5K_RX_FILTER_MCAST is already enabled */
-       if (*new_flags & FIF_ALLMULTI) {
-               mfilt[0] =  ~0;
-               mfilt[1] =  ~0;
+       mem = pci_iomap(pdev, 0, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+               ret = -EIO;
+               goto err_reg;
        }
 
-       /* This is the best we can do */
-       if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL))
-               rfilt |= AR5K_RX_FILTER_PHYERR;
+       /*
+        * Allocate hw (mac80211 main struct)
+        * and hw->priv (driver private data)
+        */
+       hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
+       if (hw == NULL) {
+               dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
+               ret = -ENOMEM;
+               goto err_map;
+       }
 
-       /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons
-       * and probes for any BSSID */
-       if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
-               rfilt |= AR5K_RX_FILTER_BEACON;
+       dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
 
-       /* FIF_CONTROL doc says that if 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, which is OK.. but
-        * but we should see if we can improve on granularity */
-       if (*new_flags & FIF_CONTROL)
-               rfilt |= AR5K_RX_FILTER_CONTROL;
+       /* Initialize driver private data */
+       SET_IEEE80211_DEV(hw, &pdev->dev);
+       hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+                   IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+                   IEEE80211_HW_SIGNAL_DBM;
 
-       /* Additional settings per mode -- this is per ath5k */
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
 
-       /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
+       hw->extra_tx_headroom = 2;
+       hw->channel_change_time = 5000;
+       sc = hw->priv;
+       sc->hw = hw;
+       sc->pdev = pdev;
 
-       switch (sc->opmode) {
-       case NL80211_IFTYPE_MESH_POINT:
-               rfilt |= AR5K_RX_FILTER_CONTROL |
-                        AR5K_RX_FILTER_BEACON |
-                        AR5K_RX_FILTER_PROBEREQ |
-                        AR5K_RX_FILTER_PROM;
-               break;
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_ADHOC:
-               rfilt |= AR5K_RX_FILTER_PROBEREQ |
-                        AR5K_RX_FILTER_BEACON;
-               break;
-       case NL80211_IFTYPE_STATION:
-               if (sc->assoc)
-                       rfilt |= AR5K_RX_FILTER_BEACON;
-       default:
-               break;
-       }
+       ath5k_debug_init_device(sc);
 
-       /* Set filters */
-       ath5k_hw_set_rx_filter(ah, rfilt);
+       /*
+        * Mark the device as detached to avoid processing
+        * interrupts until setup is complete.
+        */
+       __set_bit(ATH_STAT_INVALID, sc->status);
 
-       /* Set multicast bits */
-       ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
-       /* Set the cached hw filter flags, this will later actually
-        * be set in HW */
-       sc->filter_flags = rfilt;
+       sc->iobase = mem; /* So we can unmap it on detach */
+       sc->opmode = NL80211_IFTYPE_STATION;
+       sc->bintval = 1000;
+       mutex_init(&sc->lock);
+       spin_lock_init(&sc->rxbuflock);
+       spin_lock_init(&sc->txbuflock);
+       spin_lock_init(&sc->block);
 
-       mutex_unlock(&sc->lock);
-}
+       /* Set private data */
+       pci_set_drvdata(pdev, sc);
 
-static int
-ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
-             struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-             struct ieee80211_key_conf *key)
-{
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
-       int ret = 0;
+       /* Setup interrupt handler */
+       ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
+       if (ret) {
+               ATH5K_ERR(sc, "request_irq failed\n");
+               goto err_free;
+       }
 
-       if (modparam_nohwcrypt)
-               return -EOPNOTSUPP;
+       /* If we passed the test, malloc an ath5k_hw struct */
+       sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
+       if (!sc->ah) {
+               ret = -ENOMEM;
+               ATH5K_ERR(sc, "out of memory\n");
+               goto err_irq;
+       }
 
-       if (sc->opmode == NL80211_IFTYPE_AP)
-               return -EOPNOTSUPP;
+       sc->ah->ah_sc = sc;
+       sc->ah->ah_iobase = sc->iobase;
+       common = ath5k_hw_common(sc->ah);
+       common->ops = &ath5k_common_ops;
+       common->ah = sc->ah;
+       common->hw = hw;
+       common->cachelsz = csz << 2; /* convert to bytes */
 
-       switch (key->cipher) {
-       case WLAN_CIPHER_SUITE_WEP40:
-       case WLAN_CIPHER_SUITE_WEP104:
-       case WLAN_CIPHER_SUITE_TKIP:
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               if (sc->ah->ah_aes_support)
-                       break;
+       /* Initialize device */
+       ret = ath5k_hw_attach(sc);
+       if (ret) {
+               goto err_free_ah;
+       }
 
-               return -EOPNOTSUPP;
-       default:
-               WARN_ON(1);
-               return -EINVAL;
+       /* set up multi-rate retry capabilities */
+       if (sc->ah->ah_version == AR5K_AR5212) {
+               hw->max_rates = 4;
+               hw->max_rate_tries = 11;
        }
 
-       mutex_lock(&sc->lock);
+       /* Finish private driver data initialization */
+       ret = ath5k_attach(pdev, hw);
+       if (ret)
+               goto err_ah;
 
-       switch (cmd) {
-       case SET_KEY:
-               ret = ath5k_hw_set_key(sc->ah, key->keyidx, key,
-                                      sta ? sta->addr : NULL);
-               if (ret) {
-                       ATH5K_ERR(sc, "can't set the key\n");
-                       goto unlock;
+       ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
+                       ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
+                                       sc->ah->ah_mac_srev,
+                                       sc->ah->ah_phy_revision);
+
+       if (!sc->ah->ah_single_chip) {
+               /* Single chip radio (!RF5111) */
+               if (sc->ah->ah_radio_5ghz_revision &&
+                       !sc->ah->ah_radio_2ghz_revision) {
+                       /* No 5GHz support -> report 2GHz radio */
+                       if (!test_bit(AR5K_MODE_11A,
+                               sc->ah->ah_capabilities.cap_mode)) {
+                               ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+                                       ath5k_chip_name(AR5K_VERSION_RAD,
+                                               sc->ah->ah_radio_5ghz_revision),
+                                               sc->ah->ah_radio_5ghz_revision);
+                       /* No 2GHz support (5110 and some
+                        * 5Ghz only cards) -> report 5Ghz radio */
+                       } else if (!test_bit(AR5K_MODE_11B,
+                               sc->ah->ah_capabilities.cap_mode)) {
+                               ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+                                       ath5k_chip_name(AR5K_VERSION_RAD,
+                                               sc->ah->ah_radio_5ghz_revision),
+                                               sc->ah->ah_radio_5ghz_revision);
+                       /* Multiband radio */
+                       } else {
+                               ATH5K_INFO(sc, "RF%s multiband radio found"
+                                       " (0x%x)\n",
+                                       ath5k_chip_name(AR5K_VERSION_RAD,
+                                               sc->ah->ah_radio_5ghz_revision),
+                                               sc->ah->ah_radio_5ghz_revision);
+                       }
+               }
+               /* Multi chip radio (RF5111 - RF2111) ->
+                * report both 2GHz/5GHz radios */
+               else if (sc->ah->ah_radio_5ghz_revision &&
+                               sc->ah->ah_radio_2ghz_revision){
+                       ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+                               ath5k_chip_name(AR5K_VERSION_RAD,
+                                       sc->ah->ah_radio_5ghz_revision),
+                                       sc->ah->ah_radio_5ghz_revision);
+                       ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+                               ath5k_chip_name(AR5K_VERSION_RAD,
+                                       sc->ah->ah_radio_2ghz_revision),
+                                       sc->ah->ah_radio_2ghz_revision);
                }
-               __set_bit(key->keyidx, common->keymap);
-               key->hw_key_idx = key->keyidx;
-               key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
-                              IEEE80211_KEY_FLAG_GENERATE_MMIC);
-               break;
-       case DISABLE_KEY:
-               ath5k_hw_reset_key(sc->ah, key->keyidx);
-               __clear_bit(key->keyidx, common->keymap);
-               break;
-       default:
-               ret = -EINVAL;
-               goto unlock;
        }
 
-unlock:
-       mmiowb();
-       mutex_unlock(&sc->lock);
-       return ret;
-}
-
-static int
-ath5k_get_stats(struct ieee80211_hw *hw,
-               struct ieee80211_low_level_stats *stats)
-{
-       struct ath5k_softc *sc = hw->priv;
-
-       /* Force update */
-       ath5k_hw_update_mib_counters(sc->ah);
-
-       stats->dot11ACKFailureCount = sc->stats.ack_fail;
-       stats->dot11RTSFailureCount = sc->stats.rts_fail;
-       stats->dot11RTSSuccessCount = sc->stats.rts_ok;
-       stats->dot11FCSErrorCount = sc->stats.fcs_error;
-
-       return 0;
-}
-
-static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
-               struct survey_info *survey)
-{
-       struct ath5k_softc *sc = hw->priv;
-       struct ieee80211_conf *conf = &hw->conf;
-
-        if (idx != 0)
-               return -ENOENT;
 
-       survey->channel = conf->channel;
-       survey->filled = SURVEY_INFO_NOISE_DBM;
-       survey->noise = sc->ah->ah_noise_floor;
+       /* ready to process interrupts */
+       __clear_bit(ATH_STAT_INVALID, sc->status);
 
        return 0;
+err_ah:
+       ath5k_hw_detach(sc->ah);
+err_free_ah:
+       kfree(sc->ah);
+err_irq:
+       free_irq(pdev->irq, sc);
+err_free:
+       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 u64
-ath5k_get_tsf(struct ieee80211_hw *hw)
+static void __devexit
+ath5k_pci_remove(struct pci_dev *pdev)
 {
-       struct ath5k_softc *sc = hw->priv;
+       struct ath5k_softc *sc = pci_get_drvdata(pdev);
 
-       return ath5k_hw_get_tsf64(sc->ah);
+       ath5k_debug_finish_device(sc);
+       ath5k_detach(pdev, sc->hw);
+       ath5k_hw_detach(sc->ah);
+       kfree(sc->ah);
+       free_irq(pdev->irq, sc);
+       pci_iounmap(pdev, sc->iobase);
+       pci_release_region(pdev, 0);
+       pci_disable_device(pdev);
+       ieee80211_free_hw(sc->hw);
 }
 
-static void
-ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+#ifdef CONFIG_PM_SLEEP
+static int ath5k_pci_suspend(struct device *dev)
 {
-       struct ath5k_softc *sc = hw->priv;
+       struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
 
-       ath5k_hw_set_tsf64(sc->ah, tsf);
+       ath5k_led_off(sc);
+       return 0;
 }
 
-static void
-ath5k_reset_tsf(struct ieee80211_hw *hw)
+static int ath5k_pci_resume(struct device *dev)
 {
-       struct ath5k_softc *sc = hw->priv;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct ath5k_softc *sc = pci_get_drvdata(pdev);
 
        /*
-        * in IBSS mode we need to update the beacon timers too.
-        * this will also reset the TSF if we call it with 0
+        * 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
         */
-       if (sc->opmode == NL80211_IFTYPE_ADHOC)
-               ath5k_beacon_update_timers(sc, 0);
-       else
-               ath5k_hw_reset_tsf(sc->ah);
+       pci_write_config_byte(pdev, 0x41, 0);
+
+       ath5k_led_enable(sc);
+       return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
+#define ATH5K_PM_OPS   (&ath5k_pm_ops)
+#else
+#define ATH5K_PM_OPS   NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ath5k_pci_driver = {
+       .name           = KBUILD_MODNAME,
+       .id_table       = ath5k_pci_id_table,
+       .probe          = ath5k_pci_probe,
+       .remove         = __devexit_p(ath5k_pci_remove),
+       .driver.pm      = ATH5K_PM_OPS,
+};
+
 /*
- * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
- * this is called only once at config_bss time, for AP we do it every
- * SWBA interrupt so that the TIM will reflect buffered frames.
- *
- * Called with the beacon lock.
+ * Module init/exit functions
  */
-static int
-ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static int __init
+init_ath5k_pci(void)
 {
        int ret;
-       struct ath5k_softc *sc = hw->priv;
-       struct sk_buff *skb;
-
-       if (WARN_ON(!vif)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       skb = ieee80211_beacon_get(hw, vif);
-
-       if (!skb) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
-
-       ath5k_txbuf_free_skb(sc, sc->bbuf);
-       sc->bbuf->skb = skb;
-       ret = ath5k_beacon_setup(sc, sc->bbuf);
-       if (ret)
-               sc->bbuf->skb = NULL;
-out:
-       return ret;
-}
-
-static void
-set_beacon_filter(struct ieee80211_hw *hw, bool enable)
-{
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       u32 rfilt;
-       rfilt = ath5k_hw_get_rx_filter(ah);
-       if (enable)
-               rfilt |= AR5K_RX_FILTER_BEACON;
-       else
-               rfilt &= ~AR5K_RX_FILTER_BEACON;
-       ath5k_hw_set_rx_filter(ah, rfilt);
-       sc->filter_flags = rfilt;
-}
-
-static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
-                                   struct ieee80211_vif *vif,
-                                   struct ieee80211_bss_conf *bss_conf,
-                                   u32 changes)
-{
-       struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
-       struct ath_common *common = ath5k_hw_common(ah);
-       unsigned long flags;
-
-       mutex_lock(&sc->lock);
-       if (WARN_ON(sc->vif != vif))
-               goto unlock;
-
-       if (changes & BSS_CHANGED_BSSID) {
-               /* Cache for later use during resets */
-               memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
-               common->curaid = 0;
-               ath5k_hw_set_bssid(ah);
-               mmiowb();
-       }
-
-       if (changes & BSS_CHANGED_BEACON_INT)
-               sc->bintval = bss_conf->beacon_int;
 
-       if (changes & BSS_CHANGED_ASSOC) {
-               sc->assoc = bss_conf->assoc;
-               if (sc->opmode == NL80211_IFTYPE_STATION)
-                       set_beacon_filter(hw, sc->assoc);
-               ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
-                       AR5K_LED_ASSOC : AR5K_LED_INIT);
-               if (bss_conf->assoc) {
-                       ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
-                                 "Bss Info ASSOC %d, bssid: %pM\n",
-                                 bss_conf->aid, common->curbssid);
-                       common->curaid = bss_conf->aid;
-                       ath5k_hw_set_bssid(ah);
-                       /* Once ANI is available you would start it here */
-               }
-       }
+       ath5k_debug_init();
 
-       if (changes & BSS_CHANGED_BEACON) {
-               spin_lock_irqsave(&sc->block, flags);
-               ath5k_beacon_update(hw, vif);
-               spin_unlock_irqrestore(&sc->block, flags);
+       ret = pci_register_driver(&ath5k_pci_driver);
+       if (ret) {
+               printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
+               return ret;
        }
 
-       if (changes & BSS_CHANGED_BEACON_ENABLED)
-               sc->enable_beacon = bss_conf->enable_beacon;
-
-       if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
-                      BSS_CHANGED_BEACON_INT))
-               ath5k_beacon_config(sc);
-
- unlock:
-       mutex_unlock(&sc->lock);
+       return 0;
 }
 
-static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
+static void __exit
+exit_ath5k_pci(void)
 {
-       struct ath5k_softc *sc = hw->priv;
-       if (!sc->assoc)
-               ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
-}
+       pci_unregister_driver(&ath5k_pci_driver);
 
-static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
-{
-       struct ath5k_softc *sc = hw->priv;
-       ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
-               AR5K_LED_ASSOC : AR5K_LED_INIT);
+       ath5k_debug_finish();
 }
 
-/**
- * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
- *
- * @hw: struct ieee80211_hw pointer
- * @coverage_class: IEEE 802.11 coverage class number
- *
- * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
- * coverage class. The values are persistent, they are restored after device
- * reset.
- */
-static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
-{
-       struct ath5k_softc *sc = hw->priv;
-
-       mutex_lock(&sc->lock);
-       ath5k_hw_set_coverage_class(sc->ah, coverage_class);
-       mutex_unlock(&sc->lock);
-}
+module_init(init_ath5k_pci);
+module_exit(exit_ath5k_pci);
index dc1241f9c4e83b9a331e6f6661ce689ef9dca226..7f9d0d3018e81a2b40b026808bf6373516e0d002 100644 (file)
@@ -60,6 +60,9 @@
 #define        ATH_TXBUF       200             /* number of TX buffers */
 #define ATH_BCBUF      1               /* number of beacon buffers */
 
+#define ATH5K_TXQ_LEN_MAX      (ATH_TXBUF / 4)         /* bufs per queue */
+#define ATH5K_TXQ_LEN_LOW      (ATH5K_TXQ_LEN_MAX / 2) /* low mark */
+
 struct ath5k_buf {
        struct list_head        list;
        struct ath5k_desc       *desc;  /* virtual addr of desc */
@@ -83,6 +86,9 @@ struct ath5k_txq {
        struct list_head        q;      /* transmit queue */
        spinlock_t              lock;   /* lock on q and link */
        bool                    setup;
+       int                     txq_len; /* number of queued buffers */
+       bool                    txq_poll_mark;
+       unsigned int            txq_stuck;      /* informational counter */
 };
 
 #define ATH5K_LED_MAX_NAME_LEN 31
@@ -204,7 +210,6 @@ struct ath5k_softc {
        spinlock_t              txbuflock;
        unsigned int            txbuf_len;      /* buf count in txbuf list */
        struct ath5k_txq        txqs[AR5K_NUM_TX_QUEUES];       /* tx queues */
-       struct ath5k_txq        *txq;           /* main tx queue */
        struct tasklet_struct   txtq;           /* tx intr tasklet */
        struct ath5k_led        tx_led;         /* tx led */
 
@@ -230,6 +235,8 @@ struct ath5k_softc {
 
        struct ath5k_ani_state  ani_state;
        struct tasklet_struct   ani_tasklet;    /* ANI calibration */
+
+       struct delayed_work     tx_complete_work;
 };
 
 #define ath5k_hw_hasbssidmask(_ah) \
index 1b7c6d7fde93c1e7ca76520c503c5272247a1bd9..6583a82a0783255496ec72e17ea220153fafa296 100644 (file)
@@ -763,7 +763,7 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf,
 
        struct ath5k_txq *txq;
        struct ath5k_buf *bf, *bf0;
-       int i, n = 0;
+       int i, n;
 
        len += snprintf(buf+len, sizeof(buf)-len,
                        "available txbuffers: %d\n", sc->txbuf_len);
@@ -777,9 +777,16 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf,
                if (!txq->setup)
                        continue;
 
+               n = 0;
+               spin_lock_bh(&txq->lock);
                list_for_each_entry_safe(bf, bf0, &txq->q, list)
                        n++;
-               len += snprintf(buf+len, sizeof(buf)-len, "  len: %d\n", n);
+               spin_unlock_bh(&txq->lock);
+
+               len += snprintf(buf+len, sizeof(buf)-len,
+                               "  len: %d bufs: %d\n", txq->txq_len, n);
+               len += snprintf(buf+len, sizeof(buf)-len,
+                               "  stuck: %d\n", txq->txq_stuck);
        }
 
        if (len > sizeof(buf))
index bb2e21553d1be4d7b8e3cf47c1f86166951e2dc4..6a891c4484a0fcecae28160495924930ef998603 100644 (file)
@@ -640,197 +640,6 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
 
 }
 
-
-/*********************\
-* Key table functions *
-\*********************/
-
-/*
- * Reset a key entry on the table
- */
-int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
-{
-       unsigned int i, type;
-       u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
-
-       AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
-
-       type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry));
-
-       for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
-               ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i));
-
-       /* Reset associated MIC entry if TKIP
-        * is enabled located at offset (entry + 64) */
-       if (type == AR5K_KEYTABLE_TYPE_TKIP) {
-               AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE);
-               for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++)
-                       ath5k_hw_reg_write(ah, 0,
-                               AR5K_KEYTABLE_OFF(micentry, i));
-       }
-
-       /*
-        * Set NULL encryption on AR5212+
-        *
-        * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5)
-        *       AR5K_KEYTABLE_TYPE_NULL -> 0x00000007
-        *
-        * Note2: Windows driver (ndiswrapper) sets this to
-        *        0x00000714 instead of 0x00000007
-        */
-       if (ah->ah_version >= AR5K_AR5211) {
-               ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-                               AR5K_KEYTABLE_TYPE(entry));
-
-               if (type == AR5K_KEYTABLE_TYPE_TKIP) {
-                       ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-                               AR5K_KEYTABLE_TYPE(micentry));
-               }
-       }
-
-       return 0;
-}
-
-static
-int ath5k_keycache_type(const struct ieee80211_key_conf *key)
-{
-       switch (key->cipher) {
-       case WLAN_CIPHER_SUITE_TKIP:
-               return AR5K_KEYTABLE_TYPE_TKIP;
-       case WLAN_CIPHER_SUITE_CCMP:
-               return AR5K_KEYTABLE_TYPE_CCM;
-       case WLAN_CIPHER_SUITE_WEP40:
-               return AR5K_KEYTABLE_TYPE_40;
-       case WLAN_CIPHER_SUITE_WEP104:
-               return AR5K_KEYTABLE_TYPE_104;
-       default:
-               return -EINVAL;
-       }
-}
-
-/*
- * Set a key entry on the table
- */
-int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
-               const struct ieee80211_key_conf *key, const u8 *mac)
-{
-       unsigned int i;
-       int keylen;
-       __le32 key_v[5] = {};
-       __le32 key0 = 0, key1 = 0;
-       __le32 *rxmic, *txmic;
-       int keytype;
-       u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
-       bool is_tkip;
-       const u8 *key_ptr;
-
-       is_tkip = (key->cipher == WLAN_CIPHER_SUITE_TKIP);
-
-       /*
-        * key->keylen comes in from mac80211 in bytes.
-        * TKIP is 128 bit + 128 bit mic
-        */
-       keylen = (is_tkip) ? (128 / 8) : key->keylen;
-
-       if (entry > AR5K_KEYTABLE_SIZE ||
-               (is_tkip && micentry > AR5K_KEYTABLE_SIZE))
-               return -EOPNOTSUPP;
-
-       if (unlikely(keylen > 16))
-               return -EOPNOTSUPP;
-
-       keytype = ath5k_keycache_type(key);
-       if (keytype < 0)
-               return keytype;
-
-       /*
-        * each key block is 6 bytes wide, written as pairs of
-        * alternating 32 and 16 bit le values.
-        */
-       key_ptr = key->key;
-       for (i = 0; keylen >= 6; keylen -= 6) {
-               memcpy(&key_v[i], key_ptr, 6);
-               i += 2;
-               key_ptr += 6;
-       }
-       if (keylen)
-               memcpy(&key_v[i], key_ptr, keylen);
-
-       /* intentionally corrupt key until mic is installed */
-       if (is_tkip) {
-               key0 = key_v[0] = ~key_v[0];
-               key1 = key_v[1] = ~key_v[1];
-       }
-
-       for (i = 0; i < ARRAY_SIZE(key_v); i++)
-               ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]),
-                               AR5K_KEYTABLE_OFF(entry, i));
-
-       ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry));
-
-       if (is_tkip) {
-               /* Install rx/tx MIC */
-               rxmic = (__le32 *) &key->key[16];
-               txmic = (__le32 *) &key->key[24];
-
-               if (ah->ah_combined_mic) {
-                       key_v[0] = rxmic[0];
-                       key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16);
-                       key_v[2] = rxmic[1];
-                       key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff);
-                       key_v[4] = txmic[1];
-               } else {
-                       key_v[0] = rxmic[0];
-                       key_v[1] = 0;
-                       key_v[2] = rxmic[1];
-                       key_v[3] = 0;
-                       key_v[4] = 0;
-               }
-               for (i = 0; i < ARRAY_SIZE(key_v); i++)
-                       ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]),
-                               AR5K_KEYTABLE_OFF(micentry, i));
-
-               ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
-                       AR5K_KEYTABLE_TYPE(micentry));
-               ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry));
-               ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry));
-
-               /* restore first 2 words of key */
-               ath5k_hw_reg_write(ah, le32_to_cpu(~key0),
-                       AR5K_KEYTABLE_OFF(entry, 0));
-               ath5k_hw_reg_write(ah, le32_to_cpu(~key1),
-                       AR5K_KEYTABLE_OFF(entry, 1));
-       }
-
-       return ath5k_hw_set_key_lladdr(ah, entry, mac);
-}
-
-int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac)
-{
-       u32 low_id, high_id;
-
-        /* 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 get_unaligned_le32 and get_unaligned_le16 as we
-        * already know it.
-        */
-       if (!mac) {
-               low_id = 0xffffffff;
-               high_id = 0xffff | AR5K_KEYTABLE_VALID;
-       } else {
-               low_id = get_unaligned_le32(mac);
-               high_id = get_unaligned_le16(mac + 4) | AR5K_KEYTABLE_VALID;
-       }
-
-       ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry));
-       ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry));
-
-       return 0;
-}
-
 /**
  * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class
  *
index 984ba92c7df3781fef83719fb8f07431e36ea9be..4932bf2f35eb5a995870d0e265ab192323b2052e 100644 (file)
@@ -1377,7 +1377,7 @@ ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
 
        /* protect against divide by 0 and loss of sign bits */
        if (i_coffd == 0 || q_coffd < 2)
-               return -1;
+               return 0;
 
        i_coff = (-iq_corr) / i_coffd;
        i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
index 4186ff4c6e9c80a69d23824a356e35529c55b4c7..84c717ded1c5de8aad2a952f4d6afb507da8d261 100644 (file)
@@ -35,25 +35,59 @@ int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
        return 0;
 }
 
+/*
+ * Make sure cw is a power of 2 minus 1 and smaller than 1024
+ */
+static u16 ath5k_cw_validate(u16 cw_req)
+{
+       u32 cw = 1;
+       cw_req = min(cw_req, (u16)1023);
+
+       while (cw < cw_req)
+               cw = (cw << 1) | 1;
+
+       return cw;
+}
+
 /*
  * Set properties for a transmit queue
  */
 int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
-                               const struct ath5k_txq_info *queue_info)
+                               const struct ath5k_txq_info *qinfo)
 {
+       struct ath5k_txq_info *qi;
+
        AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
 
-       if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
+       qi = &ah->ah_txq[queue];
+
+       if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
                return -EIO;
 
-       memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
+       /* copy and validate values */
+       qi->tqi_type = qinfo->tqi_type;
+       qi->tqi_subtype = qinfo->tqi_subtype;
+       qi->tqi_flags = qinfo->tqi_flags;
+       /*
+        * According to the docs: Although the AIFS field is 8 bit wide,
+        * the maximum supported value is 0xFC. Setting it higher than that
+        * will cause the DCU to hang.
+        */
+       qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
+       qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
+       qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
+       qi->tqi_cbr_period = qinfo->tqi_cbr_period;
+       qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
+       qi->tqi_burst_time = qinfo->tqi_burst_time;
+       qi->tqi_ready_time = qinfo->tqi_ready_time;
 
        /*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)
-               ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
+       /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
+       if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
+               ((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
+                (qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
+            qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
+               qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
 
        return 0;
 }
@@ -186,7 +220,7 @@ void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
  */
 int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
 {
-       u32 cw_min, cw_max, retry_lg, retry_sh;
+       u32 retry_lg, retry_sh;
        struct ath5k_txq_info *tq = &ah->ah_txq[queue];
 
        AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
@@ -217,14 +251,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
                /* Set IFS0 */
                if (ah->ah_turbo) {
                         ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
-                               (ah->ah_aifs + tq->tqi_aifs) *
-                               AR5K_INIT_SLOT_TIME_TURBO) <<
+                               tq->tqi_aifs * AR5K_INIT_SLOT_TIME_TURBO) <<
                                AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
                                AR5K_IFS0);
                } else {
                        ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
-                               (ah->ah_aifs + tq->tqi_aifs) *
-                               AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
+                               tq->tqi_aifs * AR5K_INIT_SLOT_TIME) <<
+                               AR5K_IFS0_DIFS_S) |
                                AR5K_INIT_SIFS, AR5K_IFS0);
                }
 
@@ -247,35 +280,6 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
                        AR5K_PHY_FRAME_CTL_5210);
        }
 
-       /*
-        * Calculate cwmin/max by channel mode
-        */
-       cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
-       cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
-       ah->ah_aifs = AR5K_TUNE_AIFS;
-       /*XR is only supported on 5212*/
-       if (IS_CHAN_XR(ah->ah_current_channel) &&
-                       ah->ah_version == AR5K_AR5212) {
-               cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
-               cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
-               ah->ah_aifs = AR5K_TUNE_AIFS_XR;
-       /*B mode is not supported on 5210*/
-       } else if (IS_CHAN_B(ah->ah_current_channel) &&
-                       ah->ah_version != AR5K_AR5210) {
-               cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
-               cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
-               ah->ah_aifs = AR5K_TUNE_AIFS_11B;
-       }
-
-       cw_min = 1;
-       while (cw_min < ah->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
         */
@@ -292,7 +296,7 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
        /*No QCU/DCU [5210]*/
        if (ah->ah_version == AR5K_AR5210) {
                ath5k_hw_reg_write(ah,
-                       (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
+                       (tq->tqi_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,
@@ -314,14 +318,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
        /*===Rest is also for QCU/DCU only [5211+]===*/
 
                /*
-                * Set initial content window (cw_min/cw_max)
+                * Set contention window (cw_min/cw_max)
                 * and arbitrated interframe space (aifs)...
                 */
                ath5k_hw_reg_write(ah,
-                       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(ah->ah_aifs + tq->tqi_aifs,
-                               AR5K_DCU_LCL_IFS_AIFS),
+                       AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
+                       AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
+                       AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
                        AR5K_QUEUE_DFS_LOCAL_IFS(queue));
 
                /*
index 05ef587ad2b4eb6295c94e134dfb1a7d4d90ca53..67d63081705a60af95db2644c842f6b21ce0e63b 100644 (file)
 
 /*===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)               (ah->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
-
-/* If key type is TKIP and MIC is enabled
- * MIC key goes in offset entry + 64 */
-#define        AR5K_KEYTABLE_MIC_OFFSET        64
-
-/* 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              (ah->ah_version == AR5K_AR5210 ? \
-                                       AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211)
-
 
 /*===PHY REGISTERS===*/
 
index 35f23bdc442f243e485da8acb70d9f4fecd28977..ad57a6d2311067cc798889fef2336f9e63949511 100644 (file)
@@ -32,6 +32,14 @@ config ATH9K_DEBUGFS
 
          Also required for changing debug message flags at run time.
 
+config ATH9K_RATE_CONTROL
+       bool "Atheros ath9k rate control"
+       depends on ATH9K
+       default y
+       ---help---
+         Say Y, if you want to use the ath9k specific rate control
+         module instead of minstrel_ht.
+
 config ATH9K_HTC
        tristate "Atheros HTC based wireless cards support"
        depends on USB && MAC80211
index 4555e99839032ec10a77c5b729cbc508cdc3fa1e..aca01621c205213a1b6503047f68c6d7da6dc25e 100644 (file)
@@ -5,8 +5,8 @@ ath9k-y +=      beacon.o \
                recv.o \
                xmit.o \
                virtual.o \
-               rc.o
 
+ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o
 ath9k-$(CONFIG_PCI) += pci.o
 ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
index cc648b6ae31cef3b3a650ab7414b76fa061173e4..0496f965314fa8417ba79d30d161a221e16ad630 100644 (file)
@@ -14,6 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/kernel.h>
 #include "hw.h"
 #include "hw-ops.h"
 
@@ -48,7 +49,7 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
        {  7,  8,  0  }  /* lvl 9 */
 };
 #define ATH9K_ANI_OFDM_NUM_LEVEL \
-       (sizeof(ofdm_level_table)/sizeof(ofdm_level_table[0]))
+       ARRAY_SIZE(ofdm_level_table)
 #define ATH9K_ANI_OFDM_MAX_LEVEL \
        (ATH9K_ANI_OFDM_NUM_LEVEL-1)
 #define ATH9K_ANI_OFDM_DEF_LEVEL \
@@ -94,7 +95,7 @@ static const struct ani_cck_level_entry cck_level_table[] = {
 };
 
 #define ATH9K_ANI_CCK_NUM_LEVEL \
-       (sizeof(cck_level_table)/sizeof(cck_level_table[0]))
+       ARRAY_SIZE(cck_level_table)
 #define ATH9K_ANI_CCK_MAX_LEVEL \
        (ATH9K_ANI_CCK_NUM_LEVEL-1)
 #define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \
index 303c63da5ea384b56f4eefbdb21dfb770fddda27..94392daebaa0341192095f327dd318baa5112c66 100644 (file)
@@ -580,3 +580,53 @@ void ar9002_hw_attach_ops(struct ath_hw *ah)
        else
                ath9k_hw_attach_ani_ops_old(ah);
 }
+
+void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+       u32 modesIndex;
+       int i;
+
+       switch (chan->chanmode) {
+       case CHANNEL_A:
+       case CHANNEL_A_HT20:
+               modesIndex = 1;
+               break;
+       case CHANNEL_A_HT40PLUS:
+       case CHANNEL_A_HT40MINUS:
+               modesIndex = 2;
+               break;
+       case CHANNEL_G:
+       case CHANNEL_G_HT20:
+       case CHANNEL_B:
+               modesIndex = 4;
+               break;
+       case CHANNEL_G_HT40PLUS:
+       case CHANNEL_G_HT40MINUS:
+               modesIndex = 3;
+               break;
+
+       default:
+               return;
+       }
+
+       ENABLE_REGWRITE_BUFFER(ah);
+
+       for (i = 0; i < ah->iniModes_9271_ANI_reg.ia_rows; i++) {
+               u32 reg = INI_RA(&ah->iniModes_9271_ANI_reg, i, 0);
+               u32 val = INI_RA(&ah->iniModes_9271_ANI_reg, i, modesIndex);
+               u32 val_orig;
+
+               if (reg == AR_PHY_CCK_DETECT) {
+                       val_orig = REG_READ(ah, reg);
+                       val &= AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK;
+                       val_orig &= ~AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK;
+
+                       REG_WRITE(ah, reg, val|val_orig);
+               } else
+                       REG_WRITE(ah, reg, val);
+       }
+
+       REGWRITE_BUFFER_FLUSH(ah);
+       DISABLE_REGWRITE_BUFFER(ah);
+
+}
index adbf031fbc5a7bd37c90590870c908d3719e8be7..cd56c86927056038c77906705ea2ae4309437c87 100644 (file)
@@ -530,3 +530,38 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 
        ar9002_hw_set_nf_limits(ah);
 }
+
+void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
+                                  struct ath_hw_antcomb_conf *antconf)
+{
+       u32 regval;
+
+       regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+       antconf->main_lna_conf = (regval & AR_PHY_9285_ANT_DIV_MAIN_LNACONF) >>
+                                 AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S;
+       antconf->alt_lna_conf = (regval & AR_PHY_9285_ANT_DIV_ALT_LNACONF) >>
+                                AR_PHY_9285_ANT_DIV_ALT_LNACONF_S;
+       antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >>
+                                 AR_PHY_9285_FAST_DIV_BIAS_S;
+}
+EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_get);
+
+void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
+                                  struct ath_hw_antcomb_conf *antconf)
+{
+       u32 regval;
+
+       regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+       regval &= ~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
+                   AR_PHY_9285_ANT_DIV_ALT_LNACONF |
+                   AR_PHY_9285_FAST_DIV_BIAS);
+       regval |= ((antconf->main_lna_conf << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S)
+                  & AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+       regval |= ((antconf->alt_lna_conf << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S)
+                  & AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+       regval |= ((antconf->fast_div_bias << AR_PHY_9285_FAST_DIV_BIAS_S)
+                  & AR_PHY_9285_FAST_DIV_BIAS);
+
+       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+}
+EXPORT_SYMBOL(ath9k_hw_antdiv_comb_conf_set);
index c5151a4dd10bb01e742e7d9aaa2932519d40afd8..37663dbbcf57959f9b9afb15fe5b15e0efd613cd 100644 (file)
 #define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000
 
 #define AR_PHY_MULTICHAIN_GAIN_CTL          0x99ac
+#define AR_PHY_9285_FAST_DIV_BIAS          0x00007E00
+#define AR_PHY_9285_FAST_DIV_BIAS_S        9
 #define AR_PHY_9285_ANT_DIV_CTL_ALL         0x7f000000
 #define AR_PHY_9285_ANT_DIV_CTL             0x01000000
 #define AR_PHY_9285_ANT_DIV_CTL_S           24
index 057fb69ddf7fbc56e76b010a1cc21833fdc2b078..c4182359bee46603cffc46e9781750087f2a5f0f 100644 (file)
@@ -968,7 +968,7 @@ static int ath9k_hw_ar9300_get_eeprom_rev(struct ath_hw *ah)
 }
 
 static u8 ath9k_hw_ar9300_get_num_ant_config(struct ath_hw *ah,
-                                            enum ieee80211_band freq_band)
+                                            enum ath9k_hal_freq_band freq_band)
 {
        return 1;
 }
index a462da23e87e7735e632cdd582e9344d39ee1c35..3b424ca1ba844855b798e757e0d0727dd29e9e27 100644 (file)
@@ -616,7 +616,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
                        rxs->rs_status |= ATH9K_RXERR_DECRYPT;
                } else if (rxsp->status11 & AR_MichaelErr) {
                        rxs->rs_status |= ATH9K_RXERR_MIC;
-               }
+               } else if (rxsp->status11 & AR_KeyMiss)
+                       rxs->rs_status |= ATH9K_RXERR_DECRYPT;
        }
 
        return 0;
index f0197a6046aba3dad7d299b0f1989b528bc7ceeb..9f8e542ef47e20713c372b7f07866e5876b238ee 100644 (file)
@@ -254,7 +254,7 @@ struct ath_atx_tid {
        struct list_head buf_q;
        struct ath_node *an;
        struct ath_atx_ac *ac;
-       struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];
+       unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)];
        u16 seq_start;
        u16 seq_next;
        u16 baw_size;
@@ -345,9 +345,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb);
-bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-                      u16 tid, u16 *ssn);
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+                     u16 tid, u16 *ssn);
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
 void ath9k_enable_ps(struct ath_softc *sc);
@@ -481,6 +480,60 @@ struct ath_led {
 void ath_init_leds(struct ath_softc *sc);
 void ath_deinit_leds(struct ath_softc *sc);
 
+/* Antenna diversity/combining */
+#define ATH_ANT_RX_CURRENT_SHIFT 4
+#define ATH_ANT_RX_MAIN_SHIFT 2
+#define ATH_ANT_RX_MASK 0x3
+
+#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50
+#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100
+#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200
+#define ATH_ANT_DIV_COMB_INIT_COUNT 95
+#define ATH_ANT_DIV_COMB_MAX_COUNT 100
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
+
+#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3
+#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
+#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
+
+enum ath9k_ant_div_comb_lna_conf {
+       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+       ATH_ANT_DIV_COMB_LNA2,
+       ATH_ANT_DIV_COMB_LNA1,
+       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
+};
+
+struct ath_ant_comb {
+       u16 count;
+       u16 total_pkt_count;
+       bool scan;
+       bool scan_not_start;
+       int main_total_rssi;
+       int alt_total_rssi;
+       int alt_recv_cnt;
+       int main_recv_cnt;
+       int rssi_lna1;
+       int rssi_lna2;
+       int rssi_add;
+       int rssi_sub;
+       int rssi_first;
+       int rssi_second;
+       int rssi_third;
+       bool alt_good;
+       int quick_scan_cnt;
+       int main_conf;
+       enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
+       enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
+       int first_bias;
+       int second_bias;
+       bool first_ratio;
+       bool second_ratio;
+       unsigned long scan_start_time;
+};
+
 /********************/
 /* Main driver core */
 /********************/
@@ -509,7 +562,6 @@ void ath_deinit_leds(struct ath_softc *sc);
 #define SC_OP_RXFLUSH                BIT(7)
 #define SC_OP_LED_ASSOCIATED         BIT(8)
 #define SC_OP_LED_ON                 BIT(9)
-#define SC_OP_SCANNING               BIT(10)
 #define SC_OP_TSF_RESET              BIT(11)
 #define SC_OP_BT_PRIORITY_DETECTED   BIT(12)
 #define SC_OP_BT_SCAN               BIT(13)
@@ -597,6 +649,8 @@ struct ath_softc {
        struct ath_btcoex btcoex;
 
        struct ath_descdma txsdma;
+
+       struct ath_ant_comb ant_comb;
 };
 
 struct ath_wiphy {
@@ -663,7 +717,7 @@ static inline void ath_ahb_exit(void) {};
 void ath9k_ps_wakeup(struct ath_softc *sc);
 void ath9k_ps_restore(struct ath_softc *sc);
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 int ath9k_wiphy_add(struct ath_softc *sc);
 int ath9k_wiphy_del(struct ath_wiphy *aphy);
 void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
index 2dab64bb23a8dc4aebb51d0d9f406e222059c670..f43a2d98421c37731118c0bda90c37538608409a 100644 (file)
@@ -148,276 +148,6 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
 
-static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
-                          struct ath9k_keyval *hk, const u8 *addr,
-                          bool authenticator)
-{
-       struct ath_hw *ah = common->ah;
-       const u8 *key_rxmic;
-       const u8 *key_txmic;
-
-       key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
-       key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
-
-       if (addr == NULL) {
-               /*
-                * Group key installation - only two key cache entries are used
-                * regardless of splitmic capability since group key is only
-                * used either for TX or RX.
-                */
-               if (authenticator) {
-                       memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
-                       memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
-               } else {
-                       memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-                       memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
-               }
-               return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
-       }
-       if (!common->splitmic) {
-               /* TX and RX keys share the same key cache entry. */
-               memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-               memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
-               return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
-       }
-
-       /* Separate key cache entries for TX and RX */
-
-       /* TX key goes at first index, RX key at +32. */
-       memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
-       if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
-               /* TX MIC entry failed. No need to proceed further */
-               ath_print(common, ATH_DBG_FATAL,
-                         "Setting TX MIC Key Failed\n");
-               return 0;
-       }
-
-       memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
-       /* XXX delete tx key on failure? */
-       return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
-}
-
-static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
-{
-       int i;
-
-       for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
-               if (test_bit(i, common->keymap) ||
-                   test_bit(i + 64, common->keymap))
-                       continue; /* At least one part of TKIP key allocated */
-               if (common->splitmic &&
-                   (test_bit(i + 32, common->keymap) ||
-                    test_bit(i + 64 + 32, common->keymap)))
-                       continue; /* At least one part of TKIP key allocated */
-
-               /* Found a free slot for a TKIP key */
-               return i;
-       }
-       return -1;
-}
-
-static int ath_reserve_key_cache_slot(struct ath_common *common,
-                                     u32 cipher)
-{
-       int i;
-
-       if (cipher == WLAN_CIPHER_SUITE_TKIP)
-               return ath_reserve_key_cache_slot_tkip(common);
-
-       /* First, try to find slots that would not be available for TKIP. */
-       if (common->splitmic) {
-               for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
-                       if (!test_bit(i, common->keymap) &&
-                           (test_bit(i + 32, common->keymap) ||
-                            test_bit(i + 64, common->keymap) ||
-                            test_bit(i + 64 + 32, common->keymap)))
-                               return i;
-                       if (!test_bit(i + 32, common->keymap) &&
-                           (test_bit(i, common->keymap) ||
-                            test_bit(i + 64, common->keymap) ||
-                            test_bit(i + 64 + 32, common->keymap)))
-                               return i + 32;
-                       if (!test_bit(i + 64, common->keymap) &&
-                           (test_bit(i , common->keymap) ||
-                            test_bit(i + 32, common->keymap) ||
-                            test_bit(i + 64 + 32, common->keymap)))
-                               return i + 64;
-                       if (!test_bit(i + 64 + 32, common->keymap) &&
-                           (test_bit(i, common->keymap) ||
-                            test_bit(i + 32, common->keymap) ||
-                            test_bit(i + 64, common->keymap)))
-                               return i + 64 + 32;
-               }
-       } else {
-               for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
-                       if (!test_bit(i, common->keymap) &&
-                           test_bit(i + 64, common->keymap))
-                               return i;
-                       if (test_bit(i, common->keymap) &&
-                           !test_bit(i + 64, common->keymap))
-                               return i + 64;
-               }
-       }
-
-       /* No partially used TKIP slots, pick any available slot */
-       for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
-               /* Do not allow slots that could be needed for TKIP group keys
-                * to be used. This limitation could be removed if we know that
-                * TKIP will not be used. */
-               if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
-                       continue;
-               if (common->splitmic) {
-                       if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
-                               continue;
-                       if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
-                               continue;
-               }
-
-               if (!test_bit(i, common->keymap))
-                       return i; /* Found a free slot for a key */
-       }
-
-       /* No free slot found */
-       return -1;
-}
-
-/*
- * Configure encryption in the HW.
- */
-int ath9k_cmn_key_config(struct ath_common *common,
-                        struct ieee80211_vif *vif,
-                        struct ieee80211_sta *sta,
-                        struct ieee80211_key_conf *key)
-{
-       struct ath_hw *ah = common->ah;
-       struct ath9k_keyval hk;
-       const u8 *mac = NULL;
-       u8 gmac[ETH_ALEN];
-       int ret = 0;
-       int idx;
-
-       memset(&hk, 0, sizeof(hk));
-
-       switch (key->cipher) {
-       case WLAN_CIPHER_SUITE_WEP40:
-       case WLAN_CIPHER_SUITE_WEP104:
-               hk.kv_type = ATH9K_CIPHER_WEP;
-               break;
-       case WLAN_CIPHER_SUITE_TKIP:
-               hk.kv_type = ATH9K_CIPHER_TKIP;
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               hk.kv_type = ATH9K_CIPHER_AES_CCM;
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       hk.kv_len = key->keylen;
-       memcpy(hk.kv_val, key->key, key->keylen);
-
-       if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
-               switch (vif->type) {
-               case NL80211_IFTYPE_AP:
-                       memcpy(gmac, vif->addr, ETH_ALEN);
-                       gmac[0] |= 0x01;
-                       mac = gmac;
-                       idx = ath_reserve_key_cache_slot(common, key->cipher);
-                       break;
-               case NL80211_IFTYPE_ADHOC:
-                       if (!sta) {
-                               idx = key->keyidx;
-                               break;
-                       }
-                       memcpy(gmac, sta->addr, ETH_ALEN);
-                       gmac[0] |= 0x01;
-                       mac = gmac;
-                       idx = ath_reserve_key_cache_slot(common, key->cipher);
-                       break;
-               default:
-                       idx = key->keyidx;
-                       break;
-               }
-       } else if (key->keyidx) {
-               if (WARN_ON(!sta))
-                       return -EOPNOTSUPP;
-               mac = sta->addr;
-
-               if (vif->type != NL80211_IFTYPE_AP) {
-                       /* Only keyidx 0 should be used with unicast key, but
-                        * allow this for client mode for now. */
-                       idx = key->keyidx;
-               } else
-                       return -EIO;
-       } else {
-               if (WARN_ON(!sta))
-                       return -EOPNOTSUPP;
-               mac = sta->addr;
-
-               idx = ath_reserve_key_cache_slot(common, key->cipher);
-       }
-
-       if (idx < 0)
-               return -ENOSPC; /* no free key cache entries */
-
-       if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
-               ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
-                                     vif->type == NL80211_IFTYPE_AP);
-       else
-               ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
-
-       if (!ret)
-               return -EIO;
-
-       set_bit(idx, common->keymap);
-       if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
-               set_bit(idx + 64, common->keymap);
-               set_bit(idx, common->tkip_keymap);
-               set_bit(idx + 64, common->tkip_keymap);
-               if (common->splitmic) {
-                       set_bit(idx + 32, common->keymap);
-                       set_bit(idx + 64 + 32, common->keymap);
-                       set_bit(idx + 32, common->tkip_keymap);
-                       set_bit(idx + 64 + 32, common->tkip_keymap);
-               }
-       }
-
-       return idx;
-}
-EXPORT_SYMBOL(ath9k_cmn_key_config);
-
-/*
- * Delete Key.
- */
-void ath9k_cmn_key_delete(struct ath_common *common,
-                         struct ieee80211_key_conf *key)
-{
-       struct ath_hw *ah = common->ah;
-
-       ath9k_hw_keyreset(ah, key->hw_key_idx);
-       if (key->hw_key_idx < IEEE80211_WEP_NKID)
-               return;
-
-       clear_bit(key->hw_key_idx, common->keymap);
-       if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
-               return;
-
-       clear_bit(key->hw_key_idx + 64, common->keymap);
-
-       clear_bit(key->hw_key_idx, common->tkip_keymap);
-       clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
-
-       if (common->splitmic) {
-               ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
-               clear_bit(key->hw_key_idx + 32, common->keymap);
-               clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
-
-               clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
-               clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
-       }
-}
-EXPORT_SYMBOL(ath9k_cmn_key_delete);
-
 int ath9k_cmn_count_streams(unsigned int chainmask, int max)
 {
        int streams = 0;
index 4aa4e7dbe4d21022369eacf7f12214e73dea9f1e..fea3b3315391d3e1e0af5fc9d51a3d82ffb69004 100644 (file)
@@ -66,12 +66,6 @@ void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
                               struct ath9k_channel *ichan);
 struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
                                               struct ath_hw *ah);
-int ath9k_cmn_key_config(struct ath_common *common,
-                        struct ieee80211_vif *vif,
-                        struct ieee80211_sta *sta,
-                        struct ieee80211_key_conf *key);
-void ath9k_cmn_key_delete(struct ath_common *common,
-                         struct ieee80211_key_conf *key);
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
 void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
                                  enum ath_stomp_type stomp_type);
index 54aae931424e69aa5cecc534ee0b8f03303383c7..d65a896a421d6881fad42117dd7c4a127f22a98c 100644 (file)
@@ -492,12 +492,55 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
        unsigned int len = 0;
        int i;
        u8 addr[ETH_ALEN];
+       u32 tmp;
 
        len += snprintf(buf + len, sizeof(buf) - len,
                        "primary: %s (%s chan=%d ht=%d)\n",
                        wiphy_name(sc->pri_wiphy->hw->wiphy),
                        ath_wiphy_state_str(sc->pri_wiphy->state),
                        sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht);
+
+       put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
+       put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
+       len += snprintf(buf + len, sizeof(buf) - len,
+                       "addr: %pM\n", addr);
+       put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
+       put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
+       len += snprintf(buf + len, sizeof(buf) - len,
+                       "addrmask: %pM\n", addr);
+       tmp = ath9k_hw_getrxfilter(sc->sc_ah);
+       len += snprintf(buf + len, sizeof(buf) - len,
+                       "rfilt: 0x%x", tmp);
+       if (tmp & ATH9K_RX_FILTER_UCAST)
+               len += snprintf(buf + len, sizeof(buf) - len, " UCAST");
+       if (tmp & ATH9K_RX_FILTER_MCAST)
+               len += snprintf(buf + len, sizeof(buf) - len, " MCAST");
+       if (tmp & ATH9K_RX_FILTER_BCAST)
+               len += snprintf(buf + len, sizeof(buf) - len, " BCAST");
+       if (tmp & ATH9K_RX_FILTER_CONTROL)
+               len += snprintf(buf + len, sizeof(buf) - len, " CONTROL");
+       if (tmp & ATH9K_RX_FILTER_BEACON)
+               len += snprintf(buf + len, sizeof(buf) - len, " BEACON");
+       if (tmp & ATH9K_RX_FILTER_PROM)
+               len += snprintf(buf + len, sizeof(buf) - len, " PROM");
+       if (tmp & ATH9K_RX_FILTER_PROBEREQ)
+               len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
+       if (tmp & ATH9K_RX_FILTER_PHYERR)
+               len += snprintf(buf + len, sizeof(buf) - len, " PHYERR");
+       if (tmp & ATH9K_RX_FILTER_MYBEACON)
+               len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON");
+       if (tmp & ATH9K_RX_FILTER_COMP_BAR)
+               len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR");
+       if (tmp & ATH9K_RX_FILTER_PSPOLL)
+               len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL");
+       if (tmp & ATH9K_RX_FILTER_PHYRADAR)
+               len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
+       if (tmp & ATH9K_RX_FILTER_MCAST_BCAST_ALL)
+               len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL\n");
+       else
+               len += snprintf(buf + len, sizeof(buf) - len, "\n");
+
+       /* Put variable-length stuff down here, and check for overflows. */
        for (i = 0; i < sc->num_sec_wiphy; i++) {
                struct ath_wiphy *aphy = sc->sec_wiphy[i];
                if (aphy == NULL)
@@ -508,16 +551,6 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
                                ath_wiphy_state_str(aphy->state),
                                aphy->chan_idx, aphy->chan_is_ht);
        }
-
-       put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
-       put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "addr: %pM\n", addr);
-       put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
-       put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
-       len += snprintf(buf + len, sizeof(buf) - len,
-                       "addrmask: %pM\n", addr);
-
        if (len > sizeof(buf))
                len = sizeof(buf);
 
index 0b09db0f8e7d99b1c22c94803884a67043e2187e..3030564a0f21d070db61954adff272a7e72985ab 100644 (file)
@@ -266,6 +266,8 @@ enum eeprom_param {
        EEP_INTERNAL_REGULATOR,
        EEP_SWREG,
        EEP_PAPRD,
+       EEP_MODAL_VER,
+       EEP_ANT_DIV_CTL1,
 };
 
 enum ar5416_rates {
@@ -670,7 +672,8 @@ struct eeprom_ops {
        bool (*fill_eeprom)(struct ath_hw *hw);
        int (*get_eeprom_ver)(struct ath_hw *hw);
        int (*get_eeprom_rev)(struct ath_hw *hw);
-       u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
+       u8 (*get_num_ant_config)(struct ath_hw *hw,
+                                enum ath9k_hal_freq_band band);
        u32 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
                                      struct ath9k_channel *chan);
        void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
index 9cccd12e8f2131aa176740b7cc9e940d6e3a582f..ead8b0dd3b5340b65ea9478459eba0a952b4e9d4 100644 (file)
@@ -213,6 +213,10 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
                return 0;
        case EEP_PWR_TABLE_OFFSET:
                return AR5416_PWR_TABLE_OFFSET_DB;
+       case EEP_MODAL_VER:
+               return pModal->version;
+       case EEP_ANT_DIV_CTL1:
+               return pModal->antdiv_ctl1;
        default:
                return 0;
        }
@@ -1157,7 +1161,7 @@ static u32 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah,
 }
 
 static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah,
-                                        enum ieee80211_band freq_band)
+                                        enum ath9k_hal_freq_band freq_band)
 {
        return 1;
 }
index dff2da777312bbb0e5b1c8902b370e79c41f75b4..e6186515d05bdd8494abfbc4fac6ec3c8e4a1443 100644 (file)
@@ -1126,7 +1126,7 @@ static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah,
 }
 
 static u8 ath9k_hw_ar9287_get_num_ant_config(struct ath_hw *ah,
-                                            enum ieee80211_band freq_band)
+                                            enum ath9k_hal_freq_band freq_band)
 {
        return 1;
 }
index afa2b73ddbdde865bb91491f283dd7eceb6cdf49..23f480d4c7701ac922e0b31369fe822f6e49198d 100644 (file)
@@ -1418,11 +1418,11 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
 }
 
 static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah,
-                                         enum ieee80211_band freq_band)
+                                         enum ath9k_hal_freq_band freq_band)
 {
        struct ar5416_eeprom_def *eep = &ah->eeprom.def;
        struct modal_eep_header *pModal =
-               &(eep->modalHeader[ATH9K_HAL_FREQ_BAND_2GHZ == freq_band]);
+               &(eep->modalHeader[freq_band]);
        struct base_eep_header *pBase = &eep->baseEepHeader;
        u8 num_ant_config;
 
index 495f18950ac9d487d217f393495ffcba9562eb5d..728d904c74d7d30408231c90b46a2cfe95f3e2a9 100644 (file)
@@ -92,10 +92,10 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
        cmd->skb = skb;
        cmd->hif_dev = hif_dev;
 
-       usb_fill_int_urb(urb, hif_dev->udev,
-                        usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+       usb_fill_bulk_urb(urb, hif_dev->udev,
+                        usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE),
                         skb->data, skb->len,
-                        hif_usb_regout_cb, cmd, 1);
+                        hif_usb_regout_cb, cmd);
 
        usb_anchor_urb(urb, &hif_dev->regout_submitted);
        ret = usb_submit_urb(urb, GFP_KERNEL);
@@ -541,7 +541,8 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
                }
 
                usb_fill_int_urb(urb, hif_dev->udev,
-                                usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+                                usb_rcvbulkpipe(hif_dev->udev,
+                                                USB_REG_IN_PIPE),
                                 nskb->data, MAX_REG_IN_BUF_SIZE,
                                 ath9k_hif_usb_reg_in_cb, nskb, 1);
 
@@ -720,7 +721,8 @@ static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev)
                goto err;
 
        usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev,
-                        usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
+                        usb_rcvbulkpipe(hif_dev->udev,
+                                        USB_REG_IN_PIPE),
                         skb->data, MAX_REG_IN_BUF_SIZE,
                         ath9k_hif_usb_reg_in_cb, skb, 1);
 
@@ -822,7 +824,9 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev)
 
 static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
 {
-       int ret;
+       int ret, idx;
+       struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
+       struct usb_endpoint_descriptor *endp;
 
        /* Request firmware */
        ret = request_firmware(&hif_dev->firmware, hif_dev->fw_name,
@@ -850,6 +854,22 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
                goto err_fw_download;
        }
 
+       /* On downloading the firmware to the target, the USB descriptor of EP4
+        * is 'patched' to change the type of the endpoint to Bulk. This will
+        * bring down CPU usage during the scan period.
+        */
+       for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) {
+               endp = &alt->endpoint[idx].desc;
+               if (((endp->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
+                               == 0x04) &&
+                   ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                               == USB_ENDPOINT_XFER_INT)) {
+                       endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK;
+                       endp->bmAttributes |= USB_ENDPOINT_XFER_BULK;
+                       endp->bInterval = 0;
+               }
+       }
+
        return 0;
 
 err_fw_download:
index 695e2b088d105c4859aba2892c1305dfa6a5b655..33850c952314e5c38e0ae8cab28275f714a737cf 100644 (file)
@@ -566,7 +566,7 @@ static void ath9k_init_crypto(struct ath9k_htc_priv *priv)
         * reset the contents on initial power up.
         */
        for (i = 0; i < common->keymax; i++)
-               ath9k_hw_keyreset(priv->ah, (u16) i);
+               ath_hw_keyreset(common, (u16) i);
 }
 
 static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
@@ -601,8 +601,7 @@ static void ath9k_init_misc(struct ath9k_htc_priv *priv)
        common->tx_chainmask = priv->ah->caps.tx_chainmask;
        common->rx_chainmask = priv->ah->caps.rx_chainmask;
 
-       if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+       memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 
        priv->ah->opmode = NL80211_IFTYPE_STATION;
 }
index f4672073ac0a3e37db701c6b99d37c5f6a5181d1..5124d04b240b5d09eb21ad35f2122905dda55afc 100644 (file)
@@ -137,8 +137,6 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
        if (priv->op_flags & OP_FULL_RESET)
                fastcc = false;
 
-       /* Fiddle around with fastcc later on, for now just use full reset */
-       fastcc = false;
        ath9k_htc_ps_wakeup(priv);
        htc_stop(priv->htc);
        WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -146,9 +144,10 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
        WMI_CMD(WMI_STOP_RECV_CMDID);
 
        ath_print(common, ATH_DBG_CONFIG,
-                 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n",
+                 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
                  priv->ah->curchan->channel,
-                 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
+                 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
+                 fastcc);
 
        caldata = &priv->caldata[channel->hw_value];
        ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
@@ -1591,7 +1590,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
-               ret = ath9k_cmn_key_config(common, vif, sta, key);
+               ret = ath_key_config(common, vif, sta, key);
                if (ret >= 0) {
                        key->hw_key_idx = ret;
                        /* push IV and Michael MIC generation to stack */
@@ -1605,7 +1604,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
                }
                break;
        case DISABLE_KEY:
-               ath9k_cmn_key_delete(common, key);
+               ath_key_delete(common, key);
                break;
        default:
                ret = -EINVAL;
index 2a6e45a293a90f38b7add877b0ca231c603ebe1b..c99600aff76d463f9caa531a1d64b1e4504c12a7 100644 (file)
@@ -415,8 +415,7 @@ static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv)
        ath9k_hw_setrxfilter(ah, rfilt);
 
        /* configure bssid mask */
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               ath_hw_setbssidmask(common);
+       ath_hw_setbssidmask(common);
 
        /* configure operational mode */
        ath9k_hw_setopmode(ah);
index 3384ca1645622e4d3307d957ff193198f6b7e888..0b2ff98b6f331db9d5f5dfef406bb396df919145 100644 (file)
@@ -1258,11 +1258,13 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
            (chan->channel != ah->curchan->channel) &&
            ((chan->channelFlags & CHANNEL_ALL) ==
             (ah->curchan->channelFlags & CHANNEL_ALL)) &&
-           !AR_SREV_9280(ah)) {
+           (!AR_SREV_9280(ah) || AR_DEVID_7010(ah))) {
 
                if (ath9k_hw_channel_change(ah, chan)) {
                        ath9k_hw_loadnf(ah, ah->curchan);
                        ath9k_hw_start_nfcal(ah, true);
+                       if (AR_SREV_9271(ah))
+                               ar9002_hw_load_ani_reg(ah, chan);
                        return 0;
                }
        }
@@ -1474,283 +1476,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 }
 EXPORT_SYMBOL(ath9k_hw_reset);
 
-/************************/
-/* Key Cache Management */
-/************************/
-
-bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry)
-{
-       u32 keyType;
-
-       if (entry >= ah->caps.keycache_size) {
-               ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
-                         "keychache entry %u out of range\n", entry);
-               return false;
-       }
-
-       keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
-
-       REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
-       REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
-       REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
-
-       if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
-               u16 micentry = entry + 64;
-
-               REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
-
-       }
-
-       return true;
-}
-EXPORT_SYMBOL(ath9k_hw_keyreset);
-
-static bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac)
-{
-       u32 macHi, macLo;
-       u32 unicast_flag = AR_KEYTABLE_VALID;
-
-       if (entry >= ah->caps.keycache_size) {
-               ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
-                         "keychache entry %u out of range\n", entry);
-               return false;
-       }
-
-       if (mac != NULL) {
-               /*
-                * AR_KEYTABLE_VALID indicates that the address is a unicast
-                * address, which must match the transmitter address for
-                * decrypting frames.
-                * Not setting this bit allows the hardware to use the key
-                * for multicast frame decryption.
-                */
-               if (mac[0] & 0x01)
-                       unicast_flag = 0;
-
-               macHi = (mac[5] << 8) | mac[4];
-               macLo = (mac[3] << 24) |
-                       (mac[2] << 16) |
-                       (mac[1] << 8) |
-                       mac[0];
-               macLo >>= 1;
-               macLo |= (macHi & 1) << 31;
-               macHi >>= 1;
-       } else {
-               macLo = macHi = 0;
-       }
-       REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
-       REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag);
-
-       return true;
-}
-
-bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
-                                const struct ath9k_keyval *k,
-                                const u8 *mac)
-{
-       const struct ath9k_hw_capabilities *pCap = &ah->caps;
-       struct ath_common *common = ath9k_hw_common(ah);
-       u32 key0, key1, key2, key3, key4;
-       u32 keyType;
-
-       if (entry >= pCap->keycache_size) {
-               ath_print(common, ATH_DBG_FATAL,
-                         "keycache entry %u out of range\n", entry);
-               return false;
-       }
-
-       switch (k->kv_type) {
-       case ATH9K_CIPHER_AES_OCB:
-               keyType = AR_KEYTABLE_TYPE_AES;
-               break;
-       case ATH9K_CIPHER_AES_CCM:
-               if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) {
-                       ath_print(common, ATH_DBG_ANY,
-                                 "AES-CCM not supported by mac rev 0x%x\n",
-                                 ah->hw_version.macRev);
-                       return false;
-               }
-               keyType = AR_KEYTABLE_TYPE_CCM;
-               break;
-       case ATH9K_CIPHER_TKIP:
-               keyType = AR_KEYTABLE_TYPE_TKIP;
-               if (ATH9K_IS_MIC_ENABLED(ah)
-                   && entry + 64 >= pCap->keycache_size) {
-                       ath_print(common, ATH_DBG_ANY,
-                                 "entry %u inappropriate for TKIP\n", entry);
-                       return false;
-               }
-               break;
-       case ATH9K_CIPHER_WEP:
-               if (k->kv_len < WLAN_KEY_LEN_WEP40) {
-                       ath_print(common, ATH_DBG_ANY,
-                                 "WEP key length %u too small\n", k->kv_len);
-                       return false;
-               }
-               if (k->kv_len <= WLAN_KEY_LEN_WEP40)
-                       keyType = AR_KEYTABLE_TYPE_40;
-               else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
-                       keyType = AR_KEYTABLE_TYPE_104;
-               else
-                       keyType = AR_KEYTABLE_TYPE_128;
-               break;
-       case ATH9K_CIPHER_CLR:
-               keyType = AR_KEYTABLE_TYPE_CLR;
-               break;
-       default:
-               ath_print(common, ATH_DBG_FATAL,
-                         "cipher %u not supported\n", k->kv_type);
-               return false;
-       }
-
-       key0 = get_unaligned_le32(k->kv_val + 0);
-       key1 = get_unaligned_le16(k->kv_val + 4);
-       key2 = get_unaligned_le32(k->kv_val + 6);
-       key3 = get_unaligned_le16(k->kv_val + 10);
-       key4 = get_unaligned_le32(k->kv_val + 12);
-       if (k->kv_len <= WLAN_KEY_LEN_WEP104)
-               key4 &= 0xff;
-
-       /*
-        * Note: Key cache registers access special memory area that requires
-        * two 32-bit writes to actually update the values in the internal
-        * memory. Consequently, the exact order and pairs used here must be
-        * maintained.
-        */
-
-       if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
-               u16 micentry = entry + 64;
-
-               /*
-                * Write inverted key[47:0] first to avoid Michael MIC errors
-                * on frames that could be sent or received at the same time.
-                * The correct key will be written in the end once everything
-                * else is ready.
-                */
-               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
-
-               /* Write key[95:48] */
-               REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
-               REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
-
-               /* Write key[127:96] and key type */
-               REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
-               REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
-
-               /* Write MAC address for the entry */
-               (void) ath9k_hw_keysetmac(ah, entry, mac);
-
-               if (ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA) {
-                       /*
-                        * TKIP uses two key cache entries:
-                        * Michael MIC TX/RX keys in the same key cache entry
-                        * (idx = main index + 64):
-                        * key0 [31:0] = RX key [31:0]
-                        * key1 [15:0] = TX key [31:16]
-                        * key1 [31:16] = reserved
-                        * key2 [31:0] = RX key [63:32]
-                        * key3 [15:0] = TX key [15:0]
-                        * key3 [31:16] = reserved
-                        * key4 [31:0] = TX key [63:32]
-                        */
-                       u32 mic0, mic1, mic2, mic3, mic4;
-
-                       mic0 = get_unaligned_le32(k->kv_mic + 0);
-                       mic2 = get_unaligned_le32(k->kv_mic + 4);
-                       mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
-                       mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
-                       mic4 = get_unaligned_le32(k->kv_txmic + 4);
-
-                       /* Write RX[31:0] and TX[31:16] */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
-                       REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
-
-                       /* Write RX[63:32] and TX[15:0] */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
-                       REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
-
-                       /* Write TX[63:32] and keyType(reserved) */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
-                       REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
-                                 AR_KEYTABLE_TYPE_CLR);
-
-               } else {
-                       /*
-                        * TKIP uses four key cache entries (two for group
-                        * keys):
-                        * Michael MIC TX/RX keys are in different key cache
-                        * entries (idx = main index + 64 for TX and
-                        * main index + 32 + 96 for RX):
-                        * key0 [31:0] = TX/RX MIC key [31:0]
-                        * key1 [31:0] = reserved
-                        * key2 [31:0] = TX/RX MIC key [63:32]
-                        * key3 [31:0] = reserved
-                        * key4 [31:0] = reserved
-                        *
-                        * Upper layer code will call this function separately
-                        * for TX and RX keys when these registers offsets are
-                        * used.
-                        */
-                       u32 mic0, mic2;
-
-                       mic0 = get_unaligned_le32(k->kv_mic + 0);
-                       mic2 = get_unaligned_le32(k->kv_mic + 4);
-
-                       /* Write MIC key[31:0] */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
-                       REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
-
-                       /* Write MIC key[63:32] */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
-                       REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
-
-                       /* Write TX[63:32] and keyType(reserved) */
-                       REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
-                       REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
-                                 AR_KEYTABLE_TYPE_CLR);
-               }
-
-               /* MAC address registers are reserved for the MIC entry */
-               REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
-               REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
-
-               /*
-                * Write the correct (un-inverted) key[47:0] last to enable
-                * TKIP now that all other registers are set with correct
-                * values.
-                */
-               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
-       } else {
-               /* Write key[47:0] */
-               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
-               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
-
-               /* Write key[95:48] */
-               REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
-               REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
-
-               /* Write key[127:96] and key type */
-               REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
-               REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
-
-               /* Write MAC address for the entry */
-               (void) ath9k_hw_keysetmac(ah, entry, mac);
-       }
-
-       return true;
-}
-EXPORT_SYMBOL(ath9k_hw_set_keycache_entry);
-
 /******************************/
 /* Power Management (Chipset) */
 /******************************/
@@ -2056,6 +1781,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
 
        u16 capField = 0, eeval;
+       u8 ant_div_ctl1;
 
        eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0);
        regulatory->current_rd = eeval;
@@ -2140,24 +1866,13 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        pCap->low_5ghz_chan = 4920;
        pCap->high_5ghz_chan = 6100;
 
-       pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP;
-       pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP;
-       pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM;
-
-       pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP;
-       pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP;
-       pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM;
+       common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM;
 
        if (ah->config.ht_enable)
                pCap->hw_caps |= ATH9K_HW_CAP_HT;
        else
                pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
 
-       pCap->hw_caps |= ATH9K_HW_CAP_GTT;
-       pCap->hw_caps |= ATH9K_HW_CAP_VEOL;
-       pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK;
-       pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH;
-
        if (capField & AR_EEPROM_EEPCAP_MAXQCU)
                pCap->total_queues =
                        MS(capField, AR_EEPROM_EEPCAP_MAXQCU);
@@ -2170,8 +1885,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        else
                pCap->keycache_size = AR_KEYTABLE_SIZE;
 
-       pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
-
        if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
                pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1;
        else
@@ -2280,6 +1993,14 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        if (AR_SREV_9287_10_OR_LATER(ah) || AR_SREV_9271(ah))
                pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
 
+       if (AR_SREV_9285(ah))
+               if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
+                       ant_div_ctl1 =
+                               ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+                       if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
+                               pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+               }
+
        return 0;
 }
 
index 1601dd43989064b9bc070fa58741b7978cfbda88..df47f792cf4ed3835d2f4f3715f386e184ce4827 100644 (file)
@@ -181,29 +181,19 @@ enum wireless_mode {
 };
 
 enum ath9k_hw_caps {
-       ATH9K_HW_CAP_MIC_AESCCM                 = BIT(0),
-       ATH9K_HW_CAP_MIC_CKIP                   = BIT(1),
-       ATH9K_HW_CAP_MIC_TKIP                   = BIT(2),
-       ATH9K_HW_CAP_CIPHER_AESCCM              = BIT(3),
-       ATH9K_HW_CAP_CIPHER_CKIP                = BIT(4),
-       ATH9K_HW_CAP_CIPHER_TKIP                = BIT(5),
-       ATH9K_HW_CAP_VEOL                       = BIT(6),
-       ATH9K_HW_CAP_BSSIDMASK                  = BIT(7),
-       ATH9K_HW_CAP_MCAST_KEYSEARCH            = BIT(8),
-       ATH9K_HW_CAP_HT                         = BIT(9),
-       ATH9K_HW_CAP_GTT                        = BIT(10),
-       ATH9K_HW_CAP_FASTCC                     = BIT(11),
-       ATH9K_HW_CAP_RFSILENT                   = BIT(12),
-       ATH9K_HW_CAP_CST                        = BIT(13),
-       ATH9K_HW_CAP_ENHANCEDPM                 = BIT(14),
-       ATH9K_HW_CAP_AUTOSLEEP                  = BIT(15),
-       ATH9K_HW_CAP_4KB_SPLITTRANS             = BIT(16),
-       ATH9K_HW_CAP_EDMA                       = BIT(17),
-       ATH9K_HW_CAP_RAC_SUPPORTED              = BIT(18),
-       ATH9K_HW_CAP_LDPC                       = BIT(19),
-       ATH9K_HW_CAP_FASTCLOCK                  = BIT(20),
-       ATH9K_HW_CAP_SGI_20                     = BIT(21),
-       ATH9K_HW_CAP_PAPRD                      = BIT(22),
+       ATH9K_HW_CAP_HT                         = BIT(0),
+       ATH9K_HW_CAP_RFSILENT                   = BIT(1),
+       ATH9K_HW_CAP_CST                        = BIT(2),
+       ATH9K_HW_CAP_ENHANCEDPM                 = BIT(3),
+       ATH9K_HW_CAP_AUTOSLEEP                  = BIT(4),
+       ATH9K_HW_CAP_4KB_SPLITTRANS             = BIT(5),
+       ATH9K_HW_CAP_EDMA                       = BIT(6),
+       ATH9K_HW_CAP_RAC_SUPPORTED              = BIT(7),
+       ATH9K_HW_CAP_LDPC                       = BIT(8),
+       ATH9K_HW_CAP_FASTCLOCK                  = BIT(9),
+       ATH9K_HW_CAP_SGI_20                     = BIT(10),
+       ATH9K_HW_CAP_PAPRD                      = BIT(11),
+       ATH9K_HW_CAP_ANT_DIV_COMB               = BIT(12),
 };
 
 struct ath9k_hw_capabilities {
@@ -495,6 +485,12 @@ struct ath_gen_timer_table {
        } timer_mask;
 };
 
+struct ath_hw_antcomb_conf {
+       u8 main_lna_conf;
+       u8 alt_lna_conf;
+       u8 fast_div_bias;
+};
+
 /**
  * struct ath_hw_private_ops - callbacks used internally by hardware code
  *
@@ -874,12 +870,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 int ath9k_hw_fill_cap_info(struct ath_hw *ah);
 u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan);
 
-/* Key Cache Management */
-bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry);
-bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
-                                const struct ath9k_keyval *k,
-                                const u8 *mac);
-
 /* GPIO / RFKILL / Antennae */
 void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio);
 u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio);
@@ -888,6 +878,10 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
 u32 ath9k_hw_getdefantenna(struct ath_hw *ah);
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
+void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
+                                  struct ath_hw_antcomb_conf *antconf);
+void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
+                                  struct ath_hw_antcomb_conf *antconf);
 
 /* General Operation */
 bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
@@ -985,6 +979,7 @@ void ar9003_hw_attach_calib_ops(struct ath_hw *ah);
 void ar9002_hw_attach_ops(struct ath_hw *ah);
 void ar9003_hw_attach_ops(struct ath_hw *ah);
 
+void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan);
 /*
  * ANI work can be shared between all families but a next
  * generation implementation of ANI will be used only for AR9003 only
index 3dbff8d077667a224660464eddfcc2c1e008ea61..573899e27b3d15f54868c3da410a3acf2a5e2b2a 100644 (file)
@@ -381,7 +381,7 @@ static void ath9k_init_crypto(struct ath_softc *sc)
         * reset the contents on initial power up.
         */
        for (i = 0; i < common->keymax; i++)
-               ath9k_hw_keyreset(sc->sc_ah, (u16) i);
+               ath_hw_keyreset(common, (u16) i);
 
        /*
         * Check whether the separate key cache entries
@@ -389,8 +389,8 @@ static void ath9k_init_crypto(struct ath_softc *sc)
         * With split mic keys the number of stations is limited
         * to 27 otherwise 59.
         */
-       if (!(sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA))
-               common->splitmic = 1;
+       if (sc->sc_ah->misc_mode & AR_PCU_MIC_NEW_LOC_ENA)
+               common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED;
 }
 
 static int ath9k_init_btcoex(struct ath_softc *sc)
@@ -522,8 +522,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
        ath9k_hw_set_diversity(sc->sc_ah, true);
        sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah);
 
-       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+       memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
 
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;
 
@@ -531,6 +530,9 @@ static void ath9k_init_misc(struct ath_softc *sc)
                sc->beacon.bslot[i] = NULL;
                sc->beacon.bslot_aphy[i] = NULL;
        }
+
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+               sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
 }
 
 static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
@@ -641,7 +643,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_MESH_POINT);
 
-       hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+       if (AR_SREV_5416(sc->sc_ah))
+               hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
        hw->queues = 4;
        hw->max_rates = 4;
@@ -651,7 +654,9 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->sta_data_size = sizeof(struct ath_node);
        hw->vif_data_size = sizeof(struct ath_vif);
 
+#ifdef CONFIG_ATH9K_RATE_CONTROL
        hw->rate_control_algorithm = "ath9k_rate_control";
+#endif
 
        if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
                hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
index 0b7d1253f0c0de6fbddb6eeba3b0c23af7831b08..3efda8a8a3c1e66e1b0ed71a7a5bbad2a936bb28 100644 (file)
@@ -714,6 +714,8 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
                else if ((ads.ds_rxstatus8 & AR_MichaelErr) &&
                         rs->rs_keyix != ATH9K_RXKEYIX_INVALID)
                        rs->rs_status |= ATH9K_RXERR_MIC;
+               else if (ads.ds_rxstatus8 & AR_KeyMiss)
+                       rs->rs_status |= ATH9K_RXERR_DECRYPT;
        }
 
        return 0;
index 2633896d39983499f931b576db97bfdbe5da2e31..7c1a34d64f6debb70b23cd1c1cfe387fdb17868d 100644 (file)
@@ -660,17 +660,6 @@ struct ath9k_11n_rate_series {
        u32 RateFlags;
 };
 
-struct ath9k_keyval {
-       u8 kv_type;
-       u8 kv_pad;
-       u16 kv_len;
-       u8 kv_val[16]; /* TK */
-       u8 kv_mic[8]; /* Michael MIC key */
-       u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
-                        * supports both MIC keys in the same key cache entry;
-                        * in that case, kv_mic is the RX key) */
-};
-
 enum ath9k_key_type {
        ATH9K_KEY_TYPE_CLEAR,
        ATH9K_KEY_TYPE_WEP,
@@ -678,16 +667,6 @@ enum ath9k_key_type {
        ATH9K_KEY_TYPE_TKIP,
 };
 
-enum ath9k_cipher {
-       ATH9K_CIPHER_WEP = 0,
-       ATH9K_CIPHER_AES_OCB = 1,
-       ATH9K_CIPHER_AES_CCM = 2,
-       ATH9K_CIPHER_CKIP = 3,
-       ATH9K_CIPHER_TKIP = 4,
-       ATH9K_CIPHER_CLR = 5,
-       ATH9K_CIPHER_MIC = 127
-};
-
 struct ath_hw;
 struct ath9k_channel;
 
index 1165f909ef04605edc283a50cf48cc58cd611781..8b327bcad695f103a0ec589871109afff038bb48 100644 (file)
@@ -255,10 +255,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        ath_update_txpow(sc);
        ath9k_hw_set_interrupts(ah, ah->imask);
 
-       if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) {
-               ath_start_ani(common);
-               ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+       if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
                ath_beacon_config(sc, NULL);
+               ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+               ath_start_ani(common);
        }
 
  ps_restore:
@@ -957,7 +957,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
 
        ath_update_txpow(sc);
 
-       if (sc->sc_flags & SC_OP_BEACONS)
+       if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
                ath_beacon_config(sc, NULL);    /* restart beacons */
 
        ath9k_hw_set_interrupts(ah, ah->imask);
@@ -1156,8 +1156,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        else
                ah->imask |= ATH9K_INT_RX;
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
-               ah->imask |= ATH9K_INT_GTT;
+       ah->imask |= ATH9K_INT_GTT;
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
                ah->imask |= ATH9K_INT_CST;
@@ -1379,12 +1378,6 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
-       if (!(ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
-           sc->nvifs > 0) {
-               ret = -ENOBUFS;
-               goto out;
-       }
-
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                ic_opmode = NL80211_IFTYPE_STATION;
@@ -1414,8 +1407,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
 
        sc->nvifs++;
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               ath9k_set_bssid_mask(hw);
+       ath9k_set_bssid_mask(hw, vif);
 
        if (sc->nvifs > 1)
                goto out; /* skip global settings for secondary vif */
@@ -1562,6 +1554,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
         * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
         */
        if (changed & IEEE80211_CONF_CHANGE_PS) {
+               unsigned long flags;
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
                if (conf->flags & IEEE80211_CONF_PS) {
                        sc->ps_flags |= PS_ENABLED;
                        /*
@@ -1576,7 +1570,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                        sc->ps_enabled = false;
                        sc->ps_flags &= ~(PS_ENABLED |
                                          PS_NULLFUNC_COMPLETED);
-                       ath9k_setpower(sc, ATH9K_PM_AWAKE);
+                       ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
                        if (!(ah->caps.hw_caps &
                              ATH9K_HW_CAP_AUTOSLEEP)) {
                                ath9k_hw_setrxabort(sc->sc_ah, 0);
@@ -1591,6 +1585,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                                }
                        }
                }
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
@@ -1777,7 +1772,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
 
        switch (cmd) {
        case SET_KEY:
-               ret = ath9k_cmn_key_config(common, vif, sta, key);
+               ret = ath_key_config(common, vif, sta, key);
                if (ret >= 0) {
                        key->hw_key_idx = ret;
                        /* push IV and Michael MIC generation to stack */
@@ -1791,7 +1786,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
                }
                break;
        case DISABLE_KEY:
-               ath9k_cmn_key_delete(common, key);
+               ath_key_delete(common, key);
                break;
        default:
                ret = -EINVAL;
@@ -1975,8 +1970,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                break;
        case IEEE80211_AMPDU_TX_START:
                ath9k_ps_wakeup(sc);
-               ath_tx_aggr_start(sc, sta, tid, ssn);
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = ath_tx_aggr_start(sc, sta, tid, ssn);
+               if (!ret)
+                       ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                ath9k_ps_restore(sc);
                break;
        case IEEE80211_AMPDU_TX_STOP:
@@ -2039,7 +2035,6 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 
        aphy->state = ATH_WIPHY_SCAN;
        ath9k_wiphy_pause_all_forced(sc, aphy);
-       sc->sc_flags |= SC_OP_SCANNING;
        mutex_unlock(&sc->mutex);
 }
 
@@ -2054,7 +2049,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
 
        mutex_lock(&sc->mutex);
        aphy->state = ATH_WIPHY_ACTIVE;
-       sc->sc_flags &= ~SC_OP_SCANNING;
        mutex_unlock(&sc->mutex);
 }
 
index e724c2c1ae2a320eb5e17e7c4d419a346c5b1bdf..17969af842f6153a269acb36a7872bbf3356c05d 100644 (file)
@@ -45,9 +45,6 @@
                }                                                       \
        } while (0)
 
-#define ATH9K_IS_MIC_ENABLED(ah)                                       \
-       ((ah)->sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE)
-
 #define ANTSWAP_AB 0x0001
 #define REDUCE_CHAIN_0 0x00000050
 #define REDUCE_CHAIN_1 0x00000051
index e49be733d5461820c8dba6aec394e53700166c66..ce1cd6d858474f76f6ceebe20af14416346f29ed 100644 (file)
@@ -1320,6 +1320,22 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta,
        return caps;
 }
 
+static bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an,
+                             u8 tidno)
+{
+       struct ath_atx_tid *txtid;
+
+       if (!(sc->sc_flags & SC_OP_TXAGGR))
+               return false;
+
+       txtid = ATH_AN_2_TID(an, tidno);
+
+       if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
+                       return true;
+       return false;
+}
+
+
 /***********************************/
 /* mac80211 Rate Control callbacks */
 /***********************************/
index dc1082654501145e15cda7f0a9b17395b001d9ef..268072fd3c1c148784f23e432d1bce72de6843d5 100644 (file)
@@ -224,7 +224,18 @@ enum ath9k_internal_frame_type {
        ATH9K_IFT_UNPAUSE
 };
 
+#ifdef CONFIG_ATH9K_RATE_CONTROL
 int ath_rate_control_register(void);
 void ath_rate_control_unregister(void);
+#else
+static inline int ath_rate_control_register(void)
+{
+       return 0;
+}
+
+static inline void ath_rate_control_unregister(void)
+{
+}
+#endif
 
 #endif /* RC_H */
index 534a91bcc1d980a28d26ae3ebdef57e2c99566ca..c5e7af4f51abf05f40c76d1f3fba8a05aee38e20 100644 (file)
 
 #define SKB_CB_ATHBUF(__skb)   (*((struct ath_buf **)__skb->cb))
 
+static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+                                              int mindelta, int main_rssi_avg,
+                                              int alt_rssi_avg, int pkt_count)
+{
+       return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+               (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
+               (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+}
+
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
        return sc->ps_enabled &&
@@ -110,8 +119,7 @@ static void ath_opmode_init(struct ath_softc *sc)
        ath9k_hw_setrxfilter(ah, rfilt);
 
        /* configure bssid mask */
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               ath_hw_setbssidmask(common);
+       ath_hw_setbssidmask(common);
 
        /* configure operational mode */
        ath9k_hw_setopmode(ah);
@@ -292,7 +300,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
 
        ath_opmode_init(sc);
 
-       ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_SCANNING));
+       ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
 }
 
 static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -440,6 +448,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
                rfilt |= ATH9K_RX_FILTER_CONTROL;
 
        if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
+           (sc->nvifs <= 1) &&
            !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC))
                rfilt |= ATH9K_RX_FILTER_MYBEACON;
        else
@@ -454,9 +463,8 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
        if (conf_is_ht(&sc->hw->conf))
                rfilt |= ATH9K_RX_FILTER_COMP_BAR;
 
-       if (sc->sec_wiphy || (sc->rx.rxfilter & FIF_OTHER_BSS)) {
-               /* TODO: only needed if more than one BSSID is in use in
-                * station/adhoc mode */
+       if (sc->sec_wiphy || (sc->nvifs > 1) ||
+           (sc->rx.rxfilter & FIF_OTHER_BSS)) {
                /* The following may also be needed for other older chips */
                if (sc->sc_ah->hw_version.macVersion == AR_SREV_VERSION_9160)
                        rfilt |= ATH9K_RX_FILTER_PROM;
@@ -498,7 +506,7 @@ int ath_startrecv(struct ath_softc *sc)
 start_recv:
        spin_unlock_bh(&sc->rx.rxbuflock);
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_SCANNING));
+       ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
 
        return 0;
 }
@@ -631,7 +639,7 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
                 * No more broadcast/multicast frames to be received at this
                 * point.
                 */
-               sc->ps_flags &= ~PS_WAIT_FOR_CAB;
+               sc->ps_flags &= ~(PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON);
                ath_print(common, ATH_DBG_PS,
                          "All PS CAB frames received, back to sleep\n");
        } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) &&
@@ -1076,6 +1084,539 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common,
                rxs->flag &= ~RX_FLAG_DECRYPTED;
 }
 
+static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
+                                     struct ath_hw_antcomb_conf ant_conf,
+                                     int main_rssi_avg)
+{
+       antcomb->quick_scan_cnt = 0;
+
+       if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+               antcomb->rssi_lna2 = main_rssi_avg;
+       else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+               antcomb->rssi_lna1 = main_rssi_avg;
+
+       switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
+       case (0x10): /* LNA2 A-B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+               break;
+       case (0x20): /* LNA1 A-B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+               break;
+       case (0x21): /* LNA1 LNA2 */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case (0x12): /* LNA2 LNA1 */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case (0x13): /* LNA2 A+B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+               break;
+       case (0x23): /* LNA1 A+B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
+                               struct ath_hw_antcomb_conf *div_ant_conf,
+                               int main_rssi_avg, int alt_rssi_avg,
+                               int alt_ratio)
+{
+       /* alt_good */
+       switch (antcomb->quick_scan_cnt) {
+       case 0:
+               /* set alt to main, and alt to first conf */
+               div_ant_conf->main_lna_conf = antcomb->main_conf;
+               div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+               break;
+       case 1:
+               /* set alt to main, and alt to first conf */
+               div_ant_conf->main_lna_conf = antcomb->main_conf;
+               div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+               antcomb->rssi_first = main_rssi_avg;
+               antcomb->rssi_second = alt_rssi_avg;
+
+               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+                       /* main is LNA1 */
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               } else {
+                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+                           (alt_rssi_avg > main_rssi_avg +
+                           ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+                           (alt_rssi_avg > main_rssi_avg)) &&
+                           (antcomb->total_pkt_count > 50))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               }
+               break;
+       case 2:
+               antcomb->alt_good = false;
+               antcomb->scan_not_start = false;
+               antcomb->scan = false;
+               antcomb->rssi_first = main_rssi_avg;
+               antcomb->rssi_third = alt_rssi_avg;
+
+               if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+                       antcomb->rssi_lna1 = alt_rssi_avg;
+               else if (antcomb->second_quick_scan_conf ==
+                        ATH_ANT_DIV_COMB_LNA2)
+                       antcomb->rssi_lna2 = alt_rssi_avg;
+               else if (antcomb->second_quick_scan_conf ==
+                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+                       if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
+                               antcomb->rssi_lna2 = main_rssi_avg;
+                       else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
+                               antcomb->rssi_lna1 = main_rssi_avg;
+               }
+
+               if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
+                   ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+
+               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               } else {
+                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+                           (alt_rssi_avg > main_rssi_avg +
+                           ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+                           (alt_rssi_avg > main_rssi_avg)) &&
+                           (antcomb->total_pkt_count > 50))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               }
+
+               /* set alt to the conf with maximun ratio */
+               if (antcomb->first_ratio && antcomb->second_ratio) {
+                       if (antcomb->rssi_second > antcomb->rssi_third) {
+                               /* first alt*/
+                               if ((antcomb->first_quick_scan_conf ==
+                                   ATH_ANT_DIV_COMB_LNA1) ||
+                                   (antcomb->first_quick_scan_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2))
+                                       /* Set alt LNA1 or LNA2*/
+                                       if (div_ant_conf->main_lna_conf ==
+                                           ATH_ANT_DIV_COMB_LNA2)
+                                               div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                                       else
+                                               div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                               else
+                                       /* Set alt to A+B or A-B */
+                                       div_ant_conf->alt_lna_conf =
+                                               antcomb->first_quick_scan_conf;
+                       } else if ((antcomb->second_quick_scan_conf ==
+                                  ATH_ANT_DIV_COMB_LNA1) ||
+                                  (antcomb->second_quick_scan_conf ==
+                                  ATH_ANT_DIV_COMB_LNA2)) {
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       } else {
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                       antcomb->second_quick_scan_conf;
+                       }
+               } else if (antcomb->first_ratio) {
+                       /* first alt */
+                       if ((antcomb->first_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->first_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA2))
+                                       /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                               antcomb->first_quick_scan_conf;
+               } else if (antcomb->second_ratio) {
+                               /* second alt */
+                       if ((antcomb->second_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->second_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA2))
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                               antcomb->second_quick_scan_conf;
+               } else {
+                       /* main is largest */
+                       if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf = antcomb->main_conf;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf)
+{
+       /* Adjust the fast_div_bias based on main and alt lna conf */
+       switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
+       case (0x01): /* A-B LNA2 */
+               ant_conf->fast_div_bias = 0x3b;
+               break;
+       case (0x02): /* A-B LNA1 */
+               ant_conf->fast_div_bias = 0x3d;
+               break;
+       case (0x03): /* A-B A+B */
+               ant_conf->fast_div_bias = 0x1;
+               break;
+       case (0x10): /* LNA2 A-B */
+               ant_conf->fast_div_bias = 0x7;
+               break;
+       case (0x12): /* LNA2 LNA1 */
+               ant_conf->fast_div_bias = 0x2;
+               break;
+       case (0x13): /* LNA2 A+B */
+               ant_conf->fast_div_bias = 0x7;
+               break;
+       case (0x20): /* LNA1 A-B */
+               ant_conf->fast_div_bias = 0x6;
+               break;
+       case (0x21): /* LNA1 LNA2 */
+               ant_conf->fast_div_bias = 0x0;
+               break;
+       case (0x23): /* LNA1 A+B */
+               ant_conf->fast_div_bias = 0x6;
+               break;
+       case (0x30): /* A+B A-B */
+               ant_conf->fast_div_bias = 0x1;
+               break;
+       case (0x31): /* A+B LNA2 */
+               ant_conf->fast_div_bias = 0x3b;
+               break;
+       case (0x32): /* A+B LNA1 */
+               ant_conf->fast_div_bias = 0x3d;
+               break;
+       default:
+               break;
+       }
+}
+
+/* Antenna diversity and combining */
+static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
+{
+       struct ath_hw_antcomb_conf div_ant_conf;
+       struct ath_ant_comb *antcomb = &sc->ant_comb;
+       int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
+       int curr_main_set, curr_bias;
+       int main_rssi = rs->rs_rssi_ctl0;
+       int alt_rssi = rs->rs_rssi_ctl1;
+       int rx_ant_conf,  main_ant_conf;
+       bool short_scan = false;
+
+       rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
+                      ATH_ANT_RX_MASK;
+       main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
+                        ATH_ANT_RX_MASK;
+
+       /* Record packet only when alt_rssi is positive */
+       if (alt_rssi > 0) {
+               antcomb->total_pkt_count++;
+               antcomb->main_total_rssi += main_rssi;
+               antcomb->alt_total_rssi  += alt_rssi;
+               if (main_ant_conf == rx_ant_conf)
+                       antcomb->main_recv_cnt++;
+               else
+                       antcomb->alt_recv_cnt++;
+       }
+
+       /* Short scan check */
+       if (antcomb->scan && antcomb->alt_good) {
+               if (time_after(jiffies, antcomb->scan_start_time +
+                   msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+                       short_scan = true;
+               else
+                       if (antcomb->total_pkt_count ==
+                           ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+                               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+                                           antcomb->total_pkt_count);
+                               if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+                                       short_scan = true;
+                       }
+       }
+
+       if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
+           rs->rs_moreaggr) && !short_scan)
+               return;
+
+       if (antcomb->total_pkt_count) {
+               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+                            antcomb->total_pkt_count);
+               main_rssi_avg = (antcomb->main_total_rssi /
+                                antcomb->total_pkt_count);
+               alt_rssi_avg = (antcomb->alt_total_rssi /
+                                antcomb->total_pkt_count);
+       }
+
+
+       ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
+       curr_alt_set = div_ant_conf.alt_lna_conf;
+       curr_main_set = div_ant_conf.main_lna_conf;
+       curr_bias = div_ant_conf.fast_div_bias;
+
+       antcomb->count++;
+
+       if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
+               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+                       ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
+                                                 main_rssi_avg);
+                       antcomb->alt_good = true;
+               } else {
+                       antcomb->alt_good = false;
+               }
+
+               antcomb->count = 0;
+               antcomb->scan = true;
+               antcomb->scan_not_start = true;
+       }
+
+       if (!antcomb->scan) {
+               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+                       if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+                               /* Switch main and alt LNA */
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       }
+
+                       goto div_comb_done;
+               } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+                          (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+                       /* Set alt to another LNA */
+                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+
+                       goto div_comb_done;
+               }
+
+               if ((alt_rssi_avg < (main_rssi_avg +
+                   ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
+                       goto div_comb_done;
+       }
+
+       if (!antcomb->scan_not_start) {
+               switch (curr_alt_set) {
+               case ATH_ANT_DIV_COMB_LNA2:
+                       antcomb->rssi_lna2 = alt_rssi_avg;
+                       antcomb->rssi_lna1 = main_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A+B */
+                       div_ant_conf.main_lna_conf =
+                               ATH_ANT_DIV_COMB_LNA1;
+                       div_ant_conf.alt_lna_conf  =
+                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1:
+                       antcomb->rssi_lna1 = alt_rssi_avg;
+                       antcomb->rssi_lna2 = main_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A+B */
+                       div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       div_ant_conf.alt_lna_conf  =
+                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+                       antcomb->rssi_add = alt_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A-B */
+                       div_ant_conf.alt_lna_conf =
+                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+                       antcomb->rssi_sub = alt_rssi_avg;
+                       antcomb->scan = false;
+                       if (antcomb->rssi_lna2 >
+                           (antcomb->rssi_lna1 +
+                           ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+                               /* use LNA2 as main LNA */
+                               if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
+                                       /* set to A+B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                               } else if (antcomb->rssi_sub >
+                                          antcomb->rssi_lna1) {
+                                       /* set to A-B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                               } else {
+                                       /* set to LNA1 */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               }
+                       } else {
+                               /* use LNA1 as main LNA */
+                               if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
+                                       /* set to A+B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                               } else if (antcomb->rssi_sub >
+                                          antcomb->rssi_lna1) {
+                                       /* set to A-B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                               } else {
+                                       /* set to LNA2 */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               if (!antcomb->alt_good) {
+                       antcomb->scan_not_start = false;
+                       /* Set alt to another LNA */
+                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       }
+                       goto div_comb_done;
+               }
+       }
+
+       ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+                                          main_rssi_avg, alt_rssi_avg,
+                                          alt_ratio);
+
+       antcomb->quick_scan_cnt++;
+
+div_comb_done:
+       ath_ant_div_conf_fast_divbias(&div_ant_conf);
+
+       ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+
+       antcomb->scan_start_time = jiffies;
+       antcomb->total_pkt_count = 0;
+       antcomb->main_total_rssi = 0;
+       antcomb->alt_total_rssi = 0;
+       antcomb->main_recv_cnt = 0;
+       antcomb->alt_recv_cnt = 0;
+}
+
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
        struct ath_buf *bf;
@@ -1099,6 +1640,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
        u8 rx_status_len = ah->caps.rx_status_len;
        u64 tsf = 0;
        u32 tsf_lower = 0;
+       unsigned long flags;
 
        if (edma)
                dma_type = DMA_BIDIRECTIONAL;
@@ -1207,11 +1749,16 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        sc->rx.rxotherant = 0;
                }
 
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
                if (unlikely(ath9k_check_auto_sleep(sc) ||
                             (sc->ps_flags & (PS_WAIT_FOR_BEACON |
                                              PS_WAIT_FOR_CAB |
                                              PS_WAIT_FOR_PSPOLL_DATA))))
                        ath_rx_ps(sc, skb);
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
+               if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
+                       ath_ant_comb_scan(sc, &rs);
 
                ath_rx_send_to_mac80211(hw, sc, skb, rxs);
 
index fd20241f57d8ef8239aa3cdc62bec57198e2ec46..ec7cf5ee56bc3ac9a850f7ee56fab95251b88d6d 100644 (file)
 #include "ath9k.h"
 
 struct ath9k_vif_iter_data {
-       int count;
-       u8 *addr;
+       const u8 *hw_macaddr;
+       u8 mask[ETH_ALEN];
 };
 
 static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 {
        struct ath9k_vif_iter_data *iter_data = data;
-       u8 *nbuf;
-
-       nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
-                       GFP_ATOMIC);
-       if (nbuf == NULL)
-               return;
+       int i;
 
-       memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
-       iter_data->addr = nbuf;
-       iter_data->count++;
+       for (i = 0; i < ETH_ALEN; i++)
+               iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
 }
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath9k_vif_iter_data iter_data;
-       int i, j;
-       u8 mask[ETH_ALEN];
+       int i;
 
        /*
-        * Add primary MAC address even if it is not in active use since it
-        * will be configured to the hardware as the starting point and the
-        * BSSID mask will need to be changed if another address is active.
+        * Use the hardware MAC address as reference, the hardware uses it
+        * together with the BSSID mask when matching addresses.
         */
-       iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
-       if (iter_data.addr) {
-               memcpy(iter_data.addr, common->macaddr, ETH_ALEN);
-               iter_data.count = 1;
-       } else
-               iter_data.count = 0;
+       iter_data.hw_macaddr = common->macaddr;
+       memset(&iter_data.mask, 0xff, ETH_ALEN);
+
+       if (vif)
+               ath9k_vif_iter(&iter_data, vif->addr, vif);
 
        /* Get list of all active MAC addresses */
        spin_lock_bh(&sc->wiphy_lock);
@@ -71,31 +62,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
        }
        spin_unlock_bh(&sc->wiphy_lock);
 
-       /* Generate an address mask to cover all active addresses */
-       memset(mask, 0, ETH_ALEN);
-       for (i = 0; i < iter_data.count; i++) {
-               u8 *a1 = iter_data.addr + i * ETH_ALEN;
-               for (j = i + 1; j < iter_data.count; j++) {
-                       u8 *a2 = iter_data.addr + j * ETH_ALEN;
-                       mask[0] |= a1[0] ^ a2[0];
-                       mask[1] |= a1[1] ^ a2[1];
-                       mask[2] |= a1[2] ^ a2[2];
-                       mask[3] |= a1[3] ^ a2[3];
-                       mask[4] |= a1[4] ^ a2[4];
-                       mask[5] |= a1[5] ^ a2[5];
-               }
-       }
-
-       kfree(iter_data.addr);
-
-       /* Invert the mask and configure hardware */
-       common->bssidmask[0] = ~mask[0];
-       common->bssidmask[1] = ~mask[1];
-       common->bssidmask[2] = ~mask[2];
-       common->bssidmask[3] = ~mask[3];
-       common->bssidmask[4] = ~mask[4];
-       common->bssidmask[5] = ~mask[5];
-
+       memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
        ath_hw_setbssidmask(common);
 }
 
index 45fe9cac797141495ce3c1f0c19cba13e7f55d81..93a8bda09c251bfeef67a738249a758a12b088c4 100644 (file)
@@ -124,55 +124,11 @@ void ath9k_wmi_tasklet(unsigned long data)
 {
        struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
        struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct wmi_cmd_hdr *hdr;
-       struct wmi_swba *swba_hdr;
-       enum wmi_event_id event;
-       struct sk_buff *skb;
-       void *wmi_event;
-       unsigned long flags;
-#ifdef CONFIG_ATH9K_HTC_DEBUGFS
-       __be32 txrate;
-#endif
 
-       spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
-       skb = priv->wmi->wmi_skb;
-       spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
+       ath_print(common, ATH_DBG_WMI, "SWBA Event received\n");
 
-       hdr = (struct wmi_cmd_hdr *) skb->data;
-       event = be16_to_cpu(hdr->command_id);
-       wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+       ath9k_htc_swba(priv, priv->wmi->beacon_pending);
 
-       ath_print(common, ATH_DBG_WMI,
-                 "WMI Event: 0x%x\n", event);
-
-       switch (event) {
-       case WMI_TGT_RDY_EVENTID:
-               break;
-       case WMI_SWBA_EVENTID:
-               swba_hdr = (struct wmi_swba *) wmi_event;
-               ath9k_htc_swba(priv, swba_hdr->beacon_pending);
-               break;
-       case WMI_FATAL_EVENTID:
-               break;
-       case WMI_TXTO_EVENTID:
-               break;
-       case WMI_BMISS_EVENTID:
-               break;
-       case WMI_WLAN_TXCOMP_EVENTID:
-               break;
-       case WMI_DELBA_EVENTID:
-               break;
-       case WMI_TXRATE_EVENTID:
-#ifdef CONFIG_ATH9K_HTC_DEBUGFS
-               txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
-               priv->debug.txrate = be32_to_cpu(txrate);
-#endif
-               break;
-       default:
-               break;
-       }
-
-       kfree_skb(skb);
 }
 
 static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
@@ -191,6 +147,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
        struct wmi *wmi = (struct wmi *) priv;
        struct wmi_cmd_hdr *hdr;
        u16 cmd_id;
+       void *wmi_event;
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+       __be32 txrate;
+#endif
 
        if (unlikely(wmi->stopped))
                goto free_skb;
@@ -199,10 +159,22 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
        cmd_id = be16_to_cpu(hdr->command_id);
 
        if (cmd_id & 0x1000) {
-               spin_lock(&wmi->wmi_lock);
-               wmi->wmi_skb = skb;
-               spin_unlock(&wmi->wmi_lock);
-               tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
+               wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
+               switch (cmd_id) {
+               case WMI_SWBA_EVENTID:
+                       wmi->beacon_pending = *(u8 *)wmi_event;
+                       tasklet_schedule(&wmi->drv_priv->wmi_tasklet);
+                       break;
+               case WMI_TXRATE_EVENTID:
+#ifdef CONFIG_ATH9K_HTC_DEBUGFS
+                       txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
+                       wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
+#endif
+                       break;
+               default:
+                       break;
+               }
+               kfree_skb(skb);
                return;
        }
 
index a0bf857625df016920e0957918d127a9532bee5e..ac61074af8ac72898fb6e385e0bd63a596db21ca 100644 (file)
@@ -31,10 +31,6 @@ struct wmi_cmd_hdr {
        __be16 seq_no;
 } __packed;
 
-struct wmi_swba {
-       u8 beacon_pending;
-} __packed;
-
 enum wmi_cmd_id {
        WMI_ECHO_CMDID = 0x0001,
        WMI_ACCESS_MEMORY_CMDID,
@@ -104,7 +100,7 @@ struct wmi {
        u32 cmd_rsp_len;
        bool stopped;
 
-       struct sk_buff *wmi_skb;
+       u8 beacon_pending;
        spinlock_t wmi_lock;
 
        atomic_t mwrite_cnt;
index 457f07692ac7d2fcd3d6230c86d7acea4dc1424c..85a7323a04ef16c5ba1ee0ce0bf792d0ced0c81f 100644 (file)
@@ -61,6 +61,8 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
                              struct ath_tx_status *ts, int txok);
 static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
                             int nbad, int txok, bool update_rc);
+static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
+                             int seqno);
 
 enum {
        MCS_HT20,
@@ -143,18 +145,23 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
        struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
        struct ath_buf *bf;
        struct list_head bf_head;
-       INIT_LIST_HEAD(&bf_head);
+       struct ath_tx_status ts;
 
-       WARN_ON(!tid->paused);
+       INIT_LIST_HEAD(&bf_head);
 
+       memset(&ts, 0, sizeof(ts));
        spin_lock_bh(&txq->axq_lock);
-       tid->paused = false;
 
        while (!list_empty(&tid->buf_q)) {
                bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
-               BUG_ON(bf_isretried(bf));
                list_move_tail(&bf->list, &bf_head);
-               ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
+
+               if (bf_isretried(bf)) {
+                       ath_tx_update_baw(sc, tid, bf->bf_seqno);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
+               } else {
+                       ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
+               }
        }
 
        spin_unlock_bh(&txq->axq_lock);
@@ -168,9 +175,9 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
        index  = ATH_BA_INDEX(tid->seq_start, seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 
-       tid->tx_buf[cindex] = NULL;
+       __clear_bit(cindex, tid->tx_buf);
 
-       while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) {
+       while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) {
                INCR(tid->seq_start, IEEE80211_SEQ_MAX);
                INCR(tid->baw_head, ATH_TID_MAX_BUFS);
        }
@@ -186,9 +193,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
 
        index  = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-
-       BUG_ON(tid->tx_buf[cindex] != NULL);
-       tid->tx_buf[cindex] = bf;
+       __set_bit(cindex, tid->tx_buf);
 
        if (index >= ((tid->baw_tail - tid->baw_head) &
                (ATH_TID_MAX_BUFS - 1))) {
@@ -431,7 +436,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        list_move_tail(&bf->list, &bf_head);
                }
 
-               if (!txpending) {
+               if (!txpending || (tid->state & AGGR_CLEANUP)) {
                        /*
                         * complete the acked-ones/xretried ones; update
                         * block-ack window
@@ -510,15 +515,12 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        }
 
        if (tid->state & AGGR_CLEANUP) {
+               ath_tx_flush_tid(sc, tid);
+
                if (tid->baw_head == tid->baw_tail) {
                        tid->state &= ~AGGR_ADDBA_COMPLETE;
                        tid->state &= ~AGGR_CLEANUP;
-
-                       /* send buffered frames as singles */
-                       ath_tx_flush_tid(sc, tid);
                }
-               rcu_read_unlock();
-               return;
        }
 
        rcu_read_unlock();
@@ -785,17 +787,23 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
                 status != ATH_AGGR_BAW_CLOSED);
 }
 
-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-                      u16 tid, u16 *ssn)
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+                     u16 tid, u16 *ssn)
 {
        struct ath_atx_tid *txtid;
        struct ath_node *an;
 
        an = (struct ath_node *)sta->drv_priv;
        txtid = ATH_AN_2_TID(an, tid);
+
+       if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE))
+               return -EAGAIN;
+
        txtid->state |= AGGR_ADDBA_PROGRESS;
        txtid->paused = true;
        *ssn = txtid->seq_start;
+
+       return 0;
 }
 
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -803,12 +811,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
        struct ath_node *an = (struct ath_node *)sta->drv_priv;
        struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
        struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum];
-       struct ath_tx_status ts;
-       struct ath_buf *bf;
-       struct list_head bf_head;
-
-       memset(&ts, 0, sizeof(ts));
-       INIT_LIST_HEAD(&bf_head);
 
        if (txtid->state & AGGR_CLEANUP)
                return;
@@ -818,31 +820,22 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                return;
        }
 
-       /* drop all software retried frames and mark this TID */
        spin_lock_bh(&txq->axq_lock);
        txtid->paused = true;
-       while (!list_empty(&txtid->buf_q)) {
-               bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
-               if (!bf_isretried(bf)) {
-                       /*
-                        * NB: it's based on the assumption that
-                        * software retried frame will always stay
-                        * at the head of software queue.
-                        */
-                       break;
-               }
-               list_move_tail(&bf->list, &bf_head);
-               ath_tx_update_baw(sc, txtid, bf->bf_seqno);
-               ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
-       }
-       spin_unlock_bh(&txq->axq_lock);
 
-       if (txtid->baw_head != txtid->baw_tail) {
+       /*
+        * If frames are still being transmitted for this TID, they will be
+        * cleaned up during tx completion. To prevent race conditions, this
+        * TID can only be reused after all in-progress subframes have been
+        * completed.
+        */
+       if (txtid->baw_head != txtid->baw_tail)
                txtid->state |= AGGR_CLEANUP;
-       } else {
+       else
                txtid->state &= ~AGGR_ADDBA_COMPLETE;
-               ath_tx_flush_tid(sc, txtid);
-       }
+       spin_unlock_bh(&txq->axq_lock);
+
+       ath_tx_flush_tid(sc, txtid);
 }
 
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -862,20 +855,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
        }
 }
 
-bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno)
-{
-       struct ath_atx_tid *txtid;
-
-       if (!(sc->sc_flags & SC_OP_TXAGGR))
-               return false;
-
-       txtid = ATH_AN_2_TID(an, tidno);
-
-       if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS)))
-                       return true;
-       return false;
-}
-
 /********************/
 /* Queue Management */
 /********************/
diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig
new file mode 100644 (file)
index 0000000..c5d3a3f
--- /dev/null
@@ -0,0 +1,41 @@
+config CARL9170
+       tristate "Linux Community AR9170 802.11n USB support"
+       depends on USB && MAC80211 && EXPERIMENTAL
+       select FW_LOADER
+       select CRC32
+       help
+         This is another driver for the Atheros "otus" 802.11n USB devices.
+
+         This driver provides more features than the original,
+         but it needs a special firmware (carl9170-1.fw) to do that.
+
+         The firmware can be downloaded from our wiki here:
+         http://wireless.kernel.org/en/users/Drivers/carl9170
+
+         If you choose to build a module, it'll be called carl9170.
+
+config CARL9170_LEDS
+       bool "SoftLED Support"
+       depends on CARL9170
+       select MAC80211_LEDS
+       select LEDS_CLASS
+       select NEW_LEDS
+       default y
+       help
+         This option is necessary, if you want your device' LEDs to blink
+
+         Say Y, unless you need the LEDs for firmware debugging.
+
+config CARL9170_DEBUGFS
+       bool "DebugFS Support"
+       depends on CARL9170 && DEBUG_FS && MAC80211_DEBUGFS
+       default n
+       help
+         Export several driver and device internals to user space.
+
+         Say N.
+
+config CARL9170_WPC
+       bool
+       depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
+       default y
diff --git a/drivers/net/wireless/ath/carl9170/Makefile b/drivers/net/wireless/ath/carl9170/Makefile
new file mode 100644 (file)
index 0000000..f64ed76
--- /dev/null
@@ -0,0 +1,4 @@
+carl9170-objs := main.o usb.o cmd.o mac.o phy.o led.o fw.o tx.o rx.o
+carl9170-$(CONFIG_CARL9170_DEBUGFS) += debug.o
+
+obj-$(CONFIG_CARL9170) += carl9170.o
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
new file mode 100644 (file)
index 0000000..d7c5482
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CARL9170_H
+#define __CARL9170_H
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/usb.h>
+#ifdef CONFIG_CARL9170_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_CARL170_LEDS */
+#ifdef CONFIG_CARL9170_WPC
+#include <linux/input.h>
+#endif /* CONFIG_CARL9170_WPC */
+#include "eeprom.h"
+#include "wlan.h"
+#include "hw.h"
+#include "fwdesc.h"
+#include "fwcmd.h"
+#include "../regd.h"
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+#include "debug.h"
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+#define CARL9170FW_NAME        "carl9170-1.fw"
+
+#define PAYLOAD_MAX    (CARL9170_MAX_CMD_LEN / 4 - 1)
+
+enum carl9170_rf_init_mode {
+       CARL9170_RFI_NONE,
+       CARL9170_RFI_WARM,
+       CARL9170_RFI_COLD,
+};
+
+#define CARL9170_MAX_RX_BUFFER_SIZE            8192
+
+enum carl9170_device_state {
+       CARL9170_UNKNOWN_STATE,
+       CARL9170_STOPPED,
+       CARL9170_IDLE,
+       CARL9170_STARTED,
+};
+
+#define CARL9170_NUM_TID               16
+#define WME_BA_BMP_SIZE                        64
+#define CARL9170_TX_USER_RATE_TRIES    3
+
+#define WME_AC_BE   2
+#define WME_AC_BK   3
+#define WME_AC_VI   1
+#define WME_AC_VO   0
+
+#define TID_TO_WME_AC(_tid)                            \
+       ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
+        (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \
+        (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
+        WME_AC_VO)
+
+#define SEQ_DIFF(_start, _seq) \
+       (((_start) - (_seq)) & 0x0fff)
+#define SEQ_PREV(_seq) \
+       (((_seq) - 1) & 0x0fff)
+#define SEQ_NEXT(_seq) \
+       (((_seq) + 1) & 0x0fff)
+#define BAW_WITHIN(_start, _bawsz, _seqno) \
+       ((((_seqno) - (_start)) & 0xfff) < (_bawsz))
+
+enum carl9170_tid_state {
+       CARL9170_TID_STATE_INVALID,
+       CARL9170_TID_STATE_KILLED,
+       CARL9170_TID_STATE_SHUTDOWN,
+       CARL9170_TID_STATE_SUSPEND,
+       CARL9170_TID_STATE_PROGRESS,
+       CARL9170_TID_STATE_IDLE,
+       CARL9170_TID_STATE_XMIT,
+};
+
+#define CARL9170_BAW_BITS (2 * WME_BA_BMP_SIZE)
+#define CARL9170_BAW_SIZE (BITS_TO_LONGS(CARL9170_BAW_BITS))
+#define CARL9170_BAW_LEN (DIV_ROUND_UP(CARL9170_BAW_BITS, BITS_PER_BYTE))
+
+struct carl9170_sta_tid {
+       /* must be the first entry! */
+       struct list_head list;
+
+       /* temporary list for RCU unlink procedure */
+       struct list_head tmp_list;
+
+       /* lock for the following data structures */
+       spinlock_t lock;
+
+       unsigned int counter;
+       enum carl9170_tid_state state;
+       u8 tid;         /* TID number ( 0 - 15 ) */
+       u16 max;        /* max. AMPDU size */
+
+       u16 snx;        /* awaiting _next_ frame */
+       u16 hsn;        /* highest _queued_ sequence */
+       u16 bsn;        /* base of the tx/agg bitmap */
+       unsigned long bitmap[CARL9170_BAW_SIZE];
+
+       /* Preaggregation reorder queue */
+       struct sk_buff_head queue;
+};
+
+#define CARL9170_QUEUE_TIMEOUT         256
+#define CARL9170_BUMP_QUEUE            1000
+#define CARL9170_TX_TIMEOUT            2500
+#define CARL9170_JANITOR_DELAY         128
+#define CARL9170_QUEUE_STUCK_TIMEOUT   5500
+
+#define CARL9170_NUM_TX_AGG_MAX                30
+
+/*
+ * Tradeoff between stability/latency and speed.
+ *
+ * AR9170_TXQ_DEPTH is devised by dividing the amount of available
+ * tx buffers with the size of a full ethernet frame + overhead.
+ *
+ * Naturally: The higher the limit, the faster the device CAN send.
+ * However, even a slight over-commitment at the wrong time and the
+ * hardware is doomed to send all already-queued frames at suboptimal
+ * rates. This in turn leads to an enourmous amount of unsuccessful
+ * retries => Latency goes up, whereas the throughput goes down. CRASH!
+ */
+#define CARL9170_NUM_TX_LIMIT_HARD     ((AR9170_TXQ_DEPTH * 3) / 2)
+#define CARL9170_NUM_TX_LIMIT_SOFT     (AR9170_TXQ_DEPTH)
+
+struct carl9170_tx_queue_stats {
+       unsigned int count;
+       unsigned int limit;
+       unsigned int len;
+};
+
+struct carl9170_vif {
+       unsigned int id;
+       struct ieee80211_vif *vif;
+};
+
+struct carl9170_vif_info {
+       struct list_head list;
+       bool active;
+       unsigned int id;
+       struct sk_buff *beacon;
+       bool enable_beacon;
+};
+
+#define AR9170_NUM_RX_URBS     16
+#define AR9170_NUM_RX_URBS_MUL 2
+#define AR9170_NUM_TX_URBS     8
+#define AR9170_NUM_RX_URBS_POOL (AR9170_NUM_RX_URBS_MUL * AR9170_NUM_RX_URBS)
+
+enum carl9170_device_features {
+       CARL9170_WPS_BUTTON             = BIT(0),
+       CARL9170_ONE_LED                = BIT(1),
+};
+
+#ifdef CONFIG_CARL9170_LEDS
+struct ar9170;
+
+struct carl9170_led {
+       struct ar9170 *ar;
+       struct led_classdev l;
+       char name[32];
+       unsigned int toggled;
+       bool last_state;
+       bool registered;
+};
+#endif /* CONFIG_CARL9170_LEDS */
+
+enum carl9170_restart_reasons {
+       CARL9170_RR_NO_REASON = 0,
+       CARL9170_RR_FATAL_FIRMWARE_ERROR,
+       CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS,
+       CARL9170_RR_WATCHDOG,
+       CARL9170_RR_STUCK_TX,
+       CARL9170_RR_SLOW_SYSTEM,
+       CARL9170_RR_COMMAND_TIMEOUT,
+       CARL9170_RR_TOO_MANY_PHY_ERRORS,
+       CARL9170_RR_LOST_RSP,
+       CARL9170_RR_INVALID_RSP,
+       CARL9170_RR_USER_REQUEST,
+
+       __CARL9170_RR_LAST,
+};
+
+enum carl9170_erp_modes {
+       CARL9170_ERP_INVALID,
+       CARL9170_ERP_AUTO,
+       CARL9170_ERP_MAC80211,
+       CARL9170_ERP_OFF,
+       CARL9170_ERP_CTS,
+       CARL9170_ERP_RTS,
+       __CARL9170_ERP_NUM,
+};
+
+struct ar9170 {
+       struct ath_common common;
+       struct ieee80211_hw *hw;
+       struct mutex mutex;
+       enum carl9170_device_state state;
+       spinlock_t state_lock;
+       enum carl9170_restart_reasons last_reason;
+       bool registered;
+
+       /* USB */
+       struct usb_device *udev;
+       struct usb_interface *intf;
+       struct usb_anchor rx_anch;
+       struct usb_anchor rx_work;
+       struct usb_anchor rx_pool;
+       struct usb_anchor tx_wait;
+       struct usb_anchor tx_anch;
+       struct usb_anchor tx_cmd;
+       struct usb_anchor tx_err;
+       struct tasklet_struct usb_tasklet;
+       atomic_t tx_cmd_urbs;
+       atomic_t tx_anch_urbs;
+       atomic_t rx_anch_urbs;
+       atomic_t rx_work_urbs;
+       atomic_t rx_pool_urbs;
+       kernel_ulong_t features;
+
+       /* firmware settings */
+       struct completion fw_load_wait;
+       struct completion fw_boot_wait;
+       struct {
+               const struct carl9170fw_desc_head *desc;
+               const struct firmware *fw;
+               unsigned int offset;
+               unsigned int address;
+               unsigned int cmd_bufs;
+               unsigned int api_version;
+               unsigned int vif_num;
+               unsigned int err_counter;
+               unsigned int bug_counter;
+               u32 beacon_addr;
+               unsigned int beacon_max_len;
+               bool rx_stream;
+               bool tx_stream;
+               unsigned int mem_blocks;
+               unsigned int mem_block_size;
+               unsigned int rx_size;
+       } fw;
+
+       /* reset / stuck frames/queue detection */
+       struct work_struct restart_work;
+       unsigned int restart_counter;
+       unsigned long queue_stop_timeout[__AR9170_NUM_TXQ];
+       unsigned long max_queue_stop_timeout[__AR9170_NUM_TXQ];
+       bool needs_full_reset;
+       atomic_t pending_restarts;
+
+       /* interface mode settings */
+       struct list_head vif_list;
+       unsigned long vif_bitmap;
+       unsigned int vifs;
+       struct carl9170_vif vif_priv[AR9170_MAX_VIRTUAL_MAC];
+
+       /* beaconing */
+       spinlock_t beacon_lock;
+       unsigned int global_pretbtt;
+       unsigned int global_beacon_int;
+       struct carl9170_vif_info *beacon_iter;
+       unsigned int beacon_enabled;
+
+       /* cryptographic engine */
+       u64 usedkeys;
+       bool rx_software_decryption;
+       bool disable_offload;
+
+       /* filter settings */
+       u64 cur_mc_hash;
+       u32 cur_filter;
+       unsigned int filter_state;
+       bool sniffer_enabled;
+
+       /* MAC */
+       enum carl9170_erp_modes erp_mode;
+
+       /* PHY */
+       struct ieee80211_channel *channel;
+       int noise[4];
+       unsigned int chan_fail;
+       unsigned int total_chan_fail;
+       u8 heavy_clip;
+       u8 ht_settings;
+
+       /* power calibration data */
+       u8 power_5G_leg[4];
+       u8 power_2G_cck[4];
+       u8 power_2G_ofdm[4];
+       u8 power_5G_ht20[8];
+       u8 power_5G_ht40[8];
+       u8 power_2G_ht20[8];
+       u8 power_2G_ht40[8];
+
+#ifdef CONFIG_CARL9170_LEDS
+       /* LED */
+       struct delayed_work led_work;
+       struct carl9170_led leds[AR9170_NUM_LEDS];
+#endif /* CONFIG_CARL9170_LEDS */
+
+       /* qos queue settings */
+       spinlock_t tx_stats_lock;
+       struct carl9170_tx_queue_stats tx_stats[__AR9170_NUM_TXQ];
+       struct ieee80211_tx_queue_params edcf[5];
+       struct completion tx_flush;
+
+       /* CMD */
+       int cmd_seq;
+       int readlen;
+       u8 *readbuf;
+       spinlock_t cmd_lock;
+       struct completion cmd_wait;
+       union {
+               __le32 cmd_buf[PAYLOAD_MAX + 1];
+               struct carl9170_cmd cmd;
+               struct carl9170_rsp rsp;
+       };
+
+       /* statistics */
+       unsigned int tx_dropped;
+       unsigned int tx_ack_failures;
+       unsigned int tx_fcs_errors;
+       unsigned int tx_ampdu_timeout;
+       unsigned int rx_dropped;
+
+       /* EEPROM */
+       struct ar9170_eeprom eeprom;
+
+       /* tx queuing */
+       struct sk_buff_head tx_pending[__AR9170_NUM_TXQ];
+       struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+       struct delayed_work tx_janitor;
+       unsigned long tx_janitor_last_run;
+       bool tx_schedule;
+
+       /* tx ampdu */
+       struct work_struct ampdu_work;
+       spinlock_t tx_ampdu_list_lock;
+       struct carl9170_sta_tid *tx_ampdu_iter;
+       struct list_head tx_ampdu_list;
+       atomic_t tx_ampdu_upload;
+       atomic_t tx_ampdu_scheduler;
+       atomic_t tx_total_pending;
+       atomic_t tx_total_queued;
+       unsigned int tx_ampdu_list_len;
+       int current_density;
+       int current_factor;
+       bool tx_ampdu_schedule;
+
+       /* internal memory management */
+       spinlock_t mem_lock;
+       unsigned long *mem_bitmap;
+       atomic_t mem_free_blocks;
+       atomic_t mem_allocs;
+
+       /* rxstream mpdu merge */
+       struct ar9170_rx_head rx_plcp;
+       bool rx_has_plcp;
+       struct sk_buff *rx_failover;
+       int rx_failover_missing;
+
+#ifdef CONFIG_CARL9170_WPC
+       struct {
+               bool pbc_state;
+               struct input_dev *pbc;
+               char name[32];
+               char phys[32];
+       } wps;
+#endif /* CONFIG_CARL9170_WPC */
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+       struct carl9170_debug debug;
+       struct dentry *debug_dir;
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+       /* PSM */
+       struct work_struct ps_work;
+       struct {
+               unsigned int dtim_counter;
+               unsigned long last_beacon;
+               unsigned long last_action;
+               unsigned long last_slept;
+               unsigned int sleep_ms;
+               unsigned int off_override;
+               bool state;
+       } ps;
+};
+
+enum carl9170_ps_off_override_reasons {
+       PS_OFF_VIF      = BIT(0),
+       PS_OFF_BCN      = BIT(1),
+       PS_OFF_5GHZ     = BIT(2),
+};
+
+struct carl9170_ba_stats {
+       u8 ampdu_len;
+       u8 ampdu_ack_len;
+       bool clear;
+};
+
+struct carl9170_sta_info {
+       bool ht_sta;
+       unsigned int ampdu_max_len;
+       struct carl9170_sta_tid *agg[CARL9170_NUM_TID];
+       struct carl9170_ba_stats stats[CARL9170_NUM_TID];
+};
+
+struct carl9170_tx_info {
+       unsigned long timeout;
+       struct ar9170 *ar;
+       struct kref ref;
+};
+
+#define CHK_DEV_STATE(a, s)    (((struct ar9170 *)a)->state >= (s))
+#define IS_INITIALIZED(a)      (CHK_DEV_STATE(a, CARL9170_STOPPED))
+#define IS_ACCEPTING_CMD(a)    (CHK_DEV_STATE(a, CARL9170_IDLE))
+#define IS_STARTED(a)          (CHK_DEV_STATE(a, CARL9170_STARTED))
+
+static inline void __carl9170_set_state(struct ar9170 *ar,
+       enum carl9170_device_state newstate)
+{
+       ar->state = newstate;
+}
+
+static inline void carl9170_set_state(struct ar9170 *ar,
+       enum carl9170_device_state newstate)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ar->state_lock, flags);
+       __carl9170_set_state(ar, newstate);
+       spin_unlock_irqrestore(&ar->state_lock, flags);
+}
+
+static inline void carl9170_set_state_when(struct ar9170 *ar,
+       enum carl9170_device_state min, enum carl9170_device_state newstate)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ar->state_lock, flags);
+       if (CHK_DEV_STATE(ar, min))
+               __carl9170_set_state(ar, newstate);
+       spin_unlock_irqrestore(&ar->state_lock, flags);
+}
+
+/* exported interface */
+void *carl9170_alloc(size_t priv_size);
+int carl9170_register(struct ar9170 *ar);
+void carl9170_unregister(struct ar9170 *ar);
+void carl9170_free(struct ar9170 *ar);
+void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r);
+void carl9170_ps_check(struct ar9170 *ar);
+
+/* USB back-end */
+int carl9170_usb_open(struct ar9170 *ar);
+void carl9170_usb_stop(struct ar9170 *ar);
+void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_usb_handle_tx_err(struct ar9170 *ar);
+int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids,
+                     u32 plen, void *payload, u32 rlen, void *resp);
+int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd,
+                       const bool free_buf);
+int carl9170_usb_restart(struct ar9170 *ar);
+void carl9170_usb_reset(struct ar9170 *ar);
+
+/* MAC */
+int carl9170_init_mac(struct ar9170 *ar);
+int carl9170_set_qos(struct ar9170 *ar);
+int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hast);
+int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id,
+                            const u8 *mac);
+int carl9170_set_operating_mode(struct ar9170 *ar);
+int carl9170_set_beacon_timers(struct ar9170 *ar);
+int carl9170_set_dyn_sifs_ack(struct ar9170 *ar);
+int carl9170_set_rts_cts_rate(struct ar9170 *ar);
+int carl9170_set_ampdu_settings(struct ar9170 *ar);
+int carl9170_set_slot_time(struct ar9170 *ar);
+int carl9170_set_mac_rates(struct ar9170 *ar);
+int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry);
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit);
+int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
+       const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen);
+int carl9170_disable_key(struct ar9170 *ar, const u8 id);
+
+/* RX */
+void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len);
+void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len);
+
+/* TX */
+int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+void carl9170_tx_janitor(struct work_struct *work);
+void carl9170_tx_process_status(struct ar9170 *ar,
+                               const struct carl9170_rsp *cmd);
+void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+                       const bool success);
+void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb);
+void carl9170_tx_scheduler(struct ar9170 *ar);
+void carl9170_tx_get_skb(struct sk_buff *skb);
+int carl9170_tx_put_skb(struct sk_buff *skb);
+
+/* LEDs */
+#ifdef CONFIG_CARL9170_LEDS
+int carl9170_led_register(struct ar9170 *ar);
+void carl9170_led_unregister(struct ar9170 *ar);
+#endif /* CONFIG_CARL9170_LEDS */
+int carl9170_led_init(struct ar9170 *ar);
+int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
+
+/* PHY / RF */
+int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+       enum nl80211_channel_type bw, enum carl9170_rf_init_mode rfi);
+int carl9170_get_noisefloor(struct ar9170 *ar);
+
+/* FW */
+int carl9170_parse_firmware(struct ar9170 *ar);
+int carl9170_fw_fix_eeprom(struct ar9170 *ar);
+
+extern struct ieee80211_rate __carl9170_ratetable[];
+extern int modparam_noht;
+
+static inline struct ar9170 *carl9170_get_priv(struct carl9170_vif *carl_vif)
+{
+       return container_of(carl_vif, struct ar9170,
+                           vif_priv[carl_vif->id]);
+}
+
+static inline struct ieee80211_hdr *carl9170_get_hdr(struct sk_buff *skb)
+{
+       return (void *)((struct _carl9170_tx_superframe *)
+               skb->data)->frame_data;
+}
+
+static inline u16 get_seq_h(struct ieee80211_hdr *hdr)
+{
+       return le16_to_cpu(hdr->seq_ctrl) >> 4;
+}
+
+static inline u16 carl9170_get_seq(struct sk_buff *skb)
+{
+       return get_seq_h(carl9170_get_hdr(skb));
+}
+
+static inline u16 get_tid_h(struct ieee80211_hdr *hdr)
+{
+       return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
+static inline u16 carl9170_get_tid(struct sk_buff *skb)
+{
+       return get_tid_h(carl9170_get_hdr(skb));
+}
+
+static inline struct ieee80211_vif *
+carl9170_get_vif(struct carl9170_vif_info *priv)
+{
+       return container_of((void *)priv, struct ieee80211_vif, drv_priv);
+}
+
+/* Protected by ar->mutex or RCU */
+static inline struct ieee80211_vif *carl9170_get_main_vif(struct ar9170 *ar)
+{
+       struct carl9170_vif_info *cvif;
+
+       list_for_each_entry_rcu(cvif, &ar->vif_list, list) {
+               if (cvif->active)
+                       return carl9170_get_vif(cvif);
+       }
+
+       return NULL;
+}
+
+static inline bool is_main_vif(struct ar9170 *ar, struct ieee80211_vif *vif)
+{
+       bool ret;
+
+       rcu_read_lock();
+       ret = (carl9170_get_main_vif(ar) == vif);
+       rcu_read_unlock();
+       return ret;
+}
+
+#endif /* __CARL9170_H */
diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c
new file mode 100644 (file)
index 0000000..c21f336
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 "carl9170.h"
+#include "cmd.h"
+
+int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
+{
+       __le32 buf[2] = {
+               cpu_to_le32(reg),
+               cpu_to_le32(val),
+       };
+       int err;
+
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_WREG, sizeof(buf),
+                               (u8 *) buf, 0, NULL);
+       if (err) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "writing reg %#x "
+                               "(val %#x) failed (%d)\n", reg, val, err);
+               }
+       }
+       return err;
+}
+
+int carl9170_read_mreg(struct ar9170 *ar, const int nregs,
+                      const u32 *regs, u32 *out)
+{
+       int i, err;
+       __le32 *offs, *res;
+
+       /* abuse "out" for the register offsets, must be same length */
+       offs = (__le32 *)out;
+       for (i = 0; i < nregs; i++)
+               offs[i] = cpu_to_le32(regs[i]);
+
+       /* also use the same buffer for the input */
+       res = (__le32 *)out;
+
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
+                               4 * nregs, (u8 *)offs,
+                               4 * nregs, (u8 *)res);
+       if (err) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n",
+                                 err);
+               }
+               return err;
+       }
+
+       /* convert result to cpu endian */
+       for (i = 0; i < nregs; i++)
+               out[i] = le32_to_cpu(res[i]);
+
+       return 0;
+}
+
+int carl9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
+{
+       return carl9170_read_mreg(ar, 1, &reg, val);
+}
+
+int carl9170_echo_test(struct ar9170 *ar, const u32 v)
+{
+       u32 echores;
+       int err;
+
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_ECHO,
+                               4, (u8 *)&v,
+                               4, (u8 *)&echores);
+       if (err)
+               return err;
+
+       if (v != echores) {
+               wiphy_info(ar->hw->wiphy, "wrong echo %x != %x", v, echores);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar,
+       const enum carl9170_cmd_oids cmd, const unsigned int len)
+{
+       struct carl9170_cmd *tmp;
+
+       tmp = kzalloc(sizeof(struct carl9170_cmd_head) + len, GFP_ATOMIC);
+       if (tmp) {
+               tmp->hdr.cmd = cmd;
+               tmp->hdr.len = len;
+       }
+
+       return tmp;
+}
+
+int carl9170_reboot(struct ar9170 *ar)
+{
+       struct carl9170_cmd *cmd;
+       int err;
+
+       cmd = carl9170_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0);
+       if (!cmd)
+               return -ENOMEM;
+
+       err = __carl9170_exec_cmd(ar, (struct carl9170_cmd *)cmd, true);
+       return err;
+}
+
+int carl9170_mac_reset(struct ar9170 *ar)
+{
+       return carl9170_exec_cmd(ar, CARL9170_CMD_SWRST,
+                                0, NULL, 0, NULL);
+}
+
+int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
+                      const u32 mode, const u32 addr, const u32 len)
+{
+       struct carl9170_cmd *cmd;
+
+       cmd = carl9170_cmd_buf(ar, CARL9170_CMD_BCN_CTRL_ASYNC,
+                              sizeof(struct carl9170_bcn_ctrl_cmd));
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->bcn_ctrl.vif_id = cpu_to_le32(vif_id);
+       cmd->bcn_ctrl.mode = cpu_to_le32(mode);
+       cmd->bcn_ctrl.bcn_addr = cpu_to_le32(addr);
+       cmd->bcn_ctrl.bcn_len = cpu_to_le32(len);
+
+       return __carl9170_exec_cmd(ar, cmd, true);
+}
+
+int carl9170_powersave(struct ar9170 *ar, const bool ps)
+{
+       struct carl9170_cmd *cmd;
+       u32 state;
+
+       cmd = carl9170_cmd_buf(ar, CARL9170_CMD_PSM_ASYNC,
+                              sizeof(struct carl9170_psm));
+       if (!cmd)
+               return -ENOMEM;
+
+       if (ps) {
+               /* Sleep until next TBTT */
+               state = CARL9170_PSM_SLEEP | 1;
+       } else {
+               /* wake up immediately */
+               state = 1;
+       }
+
+       cmd->psm.state = cpu_to_le32(state);
+       return __carl9170_exec_cmd(ar, cmd, true);
+}
diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h
new file mode 100644 (file)
index 0000000..0fc83d2
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CMD_H
+#define __CMD_H
+
+#include "carl9170.h"
+
+/* basic HW access */
+int carl9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
+int carl9170_read_reg(struct ar9170 *ar, const u32 reg, u32 *val);
+int carl9170_read_mreg(struct ar9170 *ar, const int nregs,
+                      const u32 *regs, u32 *out);
+int carl9170_echo_test(struct ar9170 *ar, u32 v);
+int carl9170_reboot(struct ar9170 *ar);
+int carl9170_mac_reset(struct ar9170 *ar);
+int carl9170_powersave(struct ar9170 *ar, const bool power_on);
+int carl9170_bcn_ctrl(struct ar9170 *ar, const unsigned int vif_id,
+                      const u32 mode, const u32 addr, const u32 len);
+
+static inline int carl9170_flush_cab(struct ar9170 *ar,
+                                    const unsigned int vif_id)
+{
+       return carl9170_bcn_ctrl(ar, vif_id, CARL9170_BCN_CTRL_DRAIN, 0, 0);
+}
+
+struct carl9170_cmd *carl9170_cmd_buf(struct ar9170 *ar,
+       const enum carl9170_cmd_oids cmd, const unsigned int len);
+
+/*
+ * Macros to facilitate writing multiple registers in a single
+ * write-combining USB command. Note that when the first group
+ * fails the whole thing will fail without any others attempted,
+ * but you won't know which write in the group failed.
+ */
+#define carl9170_regwrite_begin(ar)                                    \
+do {                                                                   \
+       int __nreg = 0, __err = 0;                                      \
+       struct ar9170 *__ar = ar;
+
+#define carl9170_regwrite(r, v) do {                                   \
+       __ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r);                 \
+       __ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v);                 \
+       __nreg++;                                                       \
+       if ((__nreg >= PAYLOAD_MAX/2)) {                                \
+               if (IS_ACCEPTING_CMD(__ar))                             \
+                       __err = carl9170_exec_cmd(__ar,                 \
+                               CARL9170_CMD_WREG, 8 * __nreg,          \
+                               (u8 *) &__ar->cmd_buf[1], 0, NULL);     \
+               else                                                    \
+                       goto __regwrite_out;                            \
+                                                                       \
+               __nreg = 0;                                             \
+               if (__err)                                              \
+                       goto __regwrite_out;                            \
+       }                                                               \
+} while (0)
+
+#define carl9170_regwrite_finish()                                     \
+__regwrite_out :                                                       \
+       if (__err == 0 && __nreg) {                                     \
+               if (IS_ACCEPTING_CMD(__ar))                             \
+                       __err = carl9170_exec_cmd(__ar,                 \
+                               CARL9170_CMD_WREG, 8 * __nreg,          \
+                               (u8 *) &__ar->cmd_buf[1], 0, NULL);     \
+               __nreg = 0;                                             \
+       }
+
+#define carl9170_regwrite_result()                                     \
+       __err;                                                          \
+} while (0);
+
+
+#define carl9170_async_get_buf()                                       \
+do {                                                                   \
+       __cmd = carl9170_cmd_buf(__carl, CARL9170_CMD_WREG_ASYNC,       \
+                                CARL9170_MAX_CMD_PAYLOAD_LEN);         \
+       if (__cmd == NULL) {                                            \
+               __err = -ENOMEM;                                        \
+               goto __async_regwrite_out;                              \
+       }                                                               \
+} while (0);
+
+#define carl9170_async_regwrite_begin(carl)                            \
+do {                                                                   \
+       int __nreg = 0, __err = 0;                                      \
+       struct ar9170 *__carl = carl;                                   \
+       struct carl9170_cmd *__cmd;                                     \
+       carl9170_async_get_buf();                                       \
+
+#define carl9170_async_regwrite(r, v) do {                             \
+       __cmd->wreg.regs[__nreg].addr = cpu_to_le32(r);                 \
+       __cmd->wreg.regs[__nreg].val = cpu_to_le32(v);                  \
+       __nreg++;                                                       \
+       if ((__nreg >= PAYLOAD_MAX/2)) {                                \
+               if (IS_ACCEPTING_CMD(__carl)) {                         \
+                       __cmd->hdr.len = 8 * __nreg;                    \
+                       __err = __carl9170_exec_cmd(__carl, __cmd, true);\
+                       __cmd = NULL;                                   \
+                       carl9170_async_get_buf();                       \
+               } else {                                                \
+                       goto __async_regwrite_out;                      \
+               }                                                       \
+               __nreg = 0;                                             \
+               if (__err)                                              \
+                       goto __async_regwrite_out;                      \
+       }                                                               \
+} while (0)
+
+#define carl9170_async_regwrite_finish()                               \
+__async_regwrite_out :                                                 \
+       if (__err == 0 && __nreg) {                                     \
+               __cmd->hdr.len = 8 * __nreg;                            \
+               if (IS_ACCEPTING_CMD(__carl))                           \
+                       __err = __carl9170_exec_cmd(__carl, __cmd, true);\
+               __nreg = 0;                                             \
+       }
+
+#define carl9170_async_regwrite_result()                               \
+       __err;                                                          \
+} while (0);
+
+#endif /* __CMD_H */
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
new file mode 100644 (file)
index 0000000..19b4836
--- /dev/null
@@ -0,0 +1,906 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * debug(fs) probing
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2008-2009 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#include "carl9170.h"
+#include "cmd.h"
+
+#define ADD(buf, off, max, fmt, args...)                               \
+       off += snprintf(&buf[off], max - off, fmt, ##args);
+
+static int carl9170_debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+struct carl9170_debugfs_fops {
+       unsigned int read_bufsize;
+       mode_t attr;
+       char *(*read)(struct ar9170 *ar, char *buf, size_t bufsize,
+                     ssize_t *len);
+       ssize_t (*write)(struct ar9170 *aru, const char *buf, size_t size);
+       const struct file_operations fops;
+
+       enum carl9170_device_state req_dev_state;
+};
+
+static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf,
+                                    size_t count, loff_t *ppos)
+{
+       struct carl9170_debugfs_fops *dfops;
+       struct ar9170 *ar;
+       char *buf = NULL, *res_buf = NULL;
+       ssize_t ret = 0;
+       int err = 0;
+
+       if (!count)
+               return 0;
+
+       ar = file->private_data;
+
+       if (!ar)
+               return -ENODEV;
+       dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+
+       if (!dfops->read)
+               return -ENOSYS;
+
+       if (dfops->read_bufsize) {
+               buf = vmalloc(dfops->read_bufsize);
+               if (!buf)
+                       return -ENOMEM;
+       }
+
+       mutex_lock(&ar->mutex);
+       if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) {
+               err = -ENODEV;
+               res_buf = buf;
+               goto out_free;
+       }
+
+       res_buf = dfops->read(ar, buf, dfops->read_bufsize, &ret);
+
+       if (ret > 0)
+               err = simple_read_from_buffer(userbuf, count, ppos,
+                                             res_buf, ret);
+       else
+               err = ret;
+
+       WARN_ON_ONCE(dfops->read_bufsize && (res_buf != buf));
+
+out_free:
+       vfree(res_buf);
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static ssize_t carl9170_debugfs_write(struct file *file,
+       const char __user *userbuf, size_t count, loff_t *ppos)
+{
+       struct carl9170_debugfs_fops *dfops;
+       struct ar9170 *ar;
+       char *buf = NULL;
+       int err = 0;
+
+       if (!count)
+               return 0;
+
+       if (count > PAGE_SIZE)
+               return -E2BIG;
+
+       ar = file->private_data;
+
+       if (!ar)
+               return -ENODEV;
+       dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+
+       if (!dfops->write)
+               return -ENOSYS;
+
+       buf = vmalloc(count);
+       if (!buf)
+               return -ENOMEM;
+
+       if (copy_from_user(buf, userbuf, count)) {
+               err = -EFAULT;
+               goto out_free;
+       }
+
+       if (mutex_trylock(&ar->mutex) == 0) {
+               err = -EAGAIN;
+               goto out_free;
+       }
+
+       if (!CHK_DEV_STATE(ar, dfops->req_dev_state)) {
+               err = -ENODEV;
+               goto out_unlock;
+       }
+
+       err = dfops->write(ar, buf, count);
+       if (err)
+               goto out_unlock;
+
+out_unlock:
+       mutex_unlock(&ar->mutex);
+
+out_free:
+       vfree(buf);
+       return err;
+}
+
+#define __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize,     \
+                              _attr, _dstate)                          \
+static const struct carl9170_debugfs_fops carl_debugfs_##name ##_ops = {\
+       .read_bufsize = _read_bufsize,                                  \
+       .read = _read,                                                  \
+       .write = _write,                                                \
+       .attr = _attr,                                                  \
+       .req_dev_state = _dstate,                                       \
+       .fops = {                                                       \
+               .open   = carl9170_debugfs_open,                        \
+               .read   = carl9170_debugfs_read,                        \
+               .write  = carl9170_debugfs_write,                       \
+               .owner  = THIS_MODULE                                   \
+       },                                                              \
+}
+
+#define DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize, _attr)        \
+       __DEBUGFS_DECLARE_FILE(name, _read, _write, _read_bufsize,      \
+                              _attr, CARL9170_STARTED)                 \
+
+#define DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize)                   \
+       DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,     \
+                            NULL, _read_bufsize, S_IRUSR)
+
+#define DEBUGFS_DECLARE_WO_FILE(name)                                  \
+       DEBUGFS_DECLARE_FILE(name, NULL, carl9170_debugfs_##name ##_write,\
+                            0, S_IWUSR)
+
+#define DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize)                   \
+       DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,     \
+                            carl9170_debugfs_##name ##_write,          \
+                            _read_bufsize, S_IRUSR | S_IWUSR)
+
+#define __DEBUGFS_DECLARE_RW_FILE(name, _read_bufsize, _dstate)                \
+       __DEBUGFS_DECLARE_FILE(name, carl9170_debugfs_##name ##_read,   \
+                            carl9170_debugfs_##name ##_write,          \
+                            _read_bufsize, S_IRUSR | S_IWUSR, _dstate)
+
+#define DEBUGFS_READONLY_FILE(name, _read_bufsize, fmt, value...)      \
+static char *carl9170_debugfs_ ##name ## _read(struct ar9170 *ar,      \
+                                            char *buf, size_t buf_size,\
+                                            ssize_t *len)              \
+{                                                                      \
+       ADD(buf, *len, buf_size, fmt "\n", ##value);                    \
+       return buf;                                                     \
+}                                                                      \
+DEBUGFS_DECLARE_RO_FILE(name, _read_bufsize)
+
+static char *carl9170_debugfs_mem_usage_read(struct ar9170 *ar, char *buf,
+                                            size_t bufsize, ssize_t *len)
+{
+       ADD(buf, *len, bufsize, "jar: [");
+
+       spin_lock_bh(&ar->mem_lock);
+
+       *len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+                                 ar->mem_bitmap, ar->fw.mem_blocks);
+
+       ADD(buf, *len, bufsize, "]\n");
+
+       ADD(buf, *len, bufsize, "cookies: used:%3d / total:%3d, allocs:%d\n",
+           bitmap_weight(ar->mem_bitmap, ar->fw.mem_blocks),
+           ar->fw.mem_blocks, atomic_read(&ar->mem_allocs));
+
+       ADD(buf, *len, bufsize, "memory: free:%3d (%3d KiB) / total:%3d KiB)\n",
+           atomic_read(&ar->mem_free_blocks),
+           (atomic_read(&ar->mem_free_blocks) * ar->fw.mem_block_size) / 1024,
+           (ar->fw.mem_blocks * ar->fw.mem_block_size) / 1024);
+
+       spin_unlock_bh(&ar->mem_lock);
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(mem_usage, 512);
+
+static char *carl9170_debugfs_qos_stat_read(struct ar9170 *ar, char *buf,
+                                           size_t bufsize, ssize_t *len)
+{
+       ADD(buf, *len, bufsize, "%s QoS AC\n", modparam_noht ? "Hardware" :
+           "Software");
+
+       ADD(buf, *len, bufsize, "[     VO            VI       "
+                                "     BE            BK      ]\n");
+
+       spin_lock_bh(&ar->tx_stats_lock);
+       ADD(buf, *len, bufsize, "[length/limit  length/limit  "
+                                "length/limit  length/limit ]\n"
+                               "[   %3d/%3d       %3d/%3d    "
+                                "   %3d/%3d       %3d/%3d   ]\n\n",
+           ar->tx_stats[0].len, ar->tx_stats[0].limit,
+           ar->tx_stats[1].len, ar->tx_stats[1].limit,
+           ar->tx_stats[2].len, ar->tx_stats[2].limit,
+           ar->tx_stats[3].len, ar->tx_stats[3].limit);
+
+       ADD(buf, *len, bufsize, "[    total         total     "
+                                "    total         total    ]\n"
+                               "[%10d    %10d    %10d    %10d   ]\n\n",
+           ar->tx_stats[0].count, ar->tx_stats[1].count,
+           ar->tx_stats[2].count, ar->tx_stats[3].count);
+
+       spin_unlock_bh(&ar->tx_stats_lock);
+
+       ADD(buf, *len, bufsize, "[  pend/waittx   pend/waittx "
+                                "  pend/waittx   pend/waittx]\n"
+                               "[   %3d/%3d       %3d/%3d    "
+                                "   %3d/%3d       %3d/%3d   ]\n\n",
+           skb_queue_len(&ar->tx_pending[0]),
+           skb_queue_len(&ar->tx_status[0]),
+           skb_queue_len(&ar->tx_pending[1]),
+           skb_queue_len(&ar->tx_status[1]),
+           skb_queue_len(&ar->tx_pending[2]),
+           skb_queue_len(&ar->tx_status[2]),
+           skb_queue_len(&ar->tx_pending[3]),
+           skb_queue_len(&ar->tx_status[3]));
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(qos_stat, 512);
+
+static void carl9170_debugfs_format_frame(struct ar9170 *ar,
+       struct sk_buff *skb, const char *prefix, char *buf,
+       ssize_t *off, ssize_t bufsize)
+{
+       struct _carl9170_tx_superframe *txc = (void *) skb->data;
+       struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+       struct carl9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
+       struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+
+       ADD(buf, *off, bufsize, "%s %p, c:%2x, DA:%pM, sq:%4d, mc:%.4x, "
+           "pc:%.8x, to:%d ms\n", prefix, skb, txc->s.cookie,
+           ieee80211_get_DA(hdr), get_seq_h(hdr),
+           le16_to_cpu(txc->f.mac_control), le32_to_cpu(txc->f.phy_control),
+           jiffies_to_msecs(jiffies - arinfo->timeout));
+}
+
+
+static char *carl9170_debugfs_ampdu_state_read(struct ar9170 *ar, char *buf,
+                                              size_t bufsize, ssize_t *len)
+{
+       struct carl9170_sta_tid *iter;
+       struct sk_buff *skb;
+       int cnt = 0, fc;
+       int offset;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) {
+
+               spin_lock_bh(&iter->lock);
+               ADD(buf, *len, bufsize, "Entry: #%2d TID:%1d, BSN:%4d, "
+                   "SNX:%4d, HSN:%4d, BAW:%2d, state:%1d, toggles:%d\n",
+                   cnt, iter->tid, iter->bsn, iter->snx, iter->hsn,
+                   iter->max, iter->state, iter->counter);
+
+               ADD(buf, *len, bufsize, "\tWindow:  [");
+
+               *len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+                       iter->bitmap, CARL9170_BAW_BITS);
+
+#define BM_STR_OFF(offset)                                     \
+       ((CARL9170_BAW_BITS - (offset) - 1) / 4 +               \
+        (CARL9170_BAW_BITS - (offset) - 1) / 32 + 1)
+
+               ADD(buf, *len, bufsize, ",W]\n");
+
+               offset = BM_STR_OFF(0);
+               ADD(buf, *len, bufsize, "\tBase Seq: %*s\n", offset, "T");
+
+               offset = BM_STR_OFF(SEQ_DIFF(iter->snx, iter->bsn));
+               ADD(buf, *len, bufsize, "\tNext Seq: %*s\n", offset, "W");
+
+               offset = BM_STR_OFF(((int)iter->hsn - (int)iter->bsn) %
+                                    CARL9170_BAW_BITS);
+               ADD(buf, *len, bufsize, "\tLast Seq: %*s\n", offset, "N");
+
+               ADD(buf, *len, bufsize, "\tPre-Aggregation reorder buffer: "
+                   " currently queued:%d\n", skb_queue_len(&iter->queue));
+
+               fc = 0;
+               skb_queue_walk(&iter->queue, skb) {
+                       char prefix[32];
+
+                       snprintf(prefix, sizeof(prefix), "\t\t%3d :", fc);
+                       carl9170_debugfs_format_frame(ar, skb, prefix, buf,
+                                                     len, bufsize);
+
+                       fc++;
+               }
+               spin_unlock_bh(&iter->lock);
+               cnt++;
+       }
+       rcu_read_unlock();
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(ampdu_state, 8000);
+
+static void carl9170_debugfs_queue_dump(struct ar9170 *ar, char *buf,
+       ssize_t *len, size_t bufsize, struct sk_buff_head *queue)
+{
+       struct sk_buff *skb;
+       char prefix[16];
+       int fc = 0;
+
+       spin_lock_bh(&queue->lock);
+       skb_queue_walk(queue, skb) {
+               snprintf(prefix, sizeof(prefix), "%3d :", fc);
+               carl9170_debugfs_format_frame(ar, skb, prefix, buf,
+                                             len, bufsize);
+               fc++;
+       }
+       spin_unlock_bh(&queue->lock);
+}
+
+#define DEBUGFS_QUEUE_DUMP(q, qi)                                      \
+static char *carl9170_debugfs_##q ##_##qi ##_read(struct ar9170 *ar,   \
+       char *buf, size_t bufsize, ssize_t *len)                        \
+{                                                                      \
+       carl9170_debugfs_queue_dump(ar, buf, len, bufsize, &ar->q[qi]); \
+       return buf;                                                     \
+}                                                                      \
+DEBUGFS_DECLARE_RO_FILE(q##_##qi, 8000);
+
+static char *carl9170_debugfs_sta_psm_read(struct ar9170 *ar, char *buf,
+                                          size_t bufsize, ssize_t *len)
+{
+       ADD(buf, *len, bufsize, "psm state: %s\n", (ar->ps.off_override ?
+           "FORCE CAM" : (ar->ps.state ? "PSM" : "CAM")));
+
+       ADD(buf, *len, bufsize, "sleep duration: %d ms.\n", ar->ps.sleep_ms);
+       ADD(buf, *len, bufsize, "last power-state transition: %d ms ago.\n",
+           jiffies_to_msecs(jiffies - ar->ps.last_action));
+       ADD(buf, *len, bufsize, "last CAM->PSM transition: %d ms ago.\n",
+           jiffies_to_msecs(jiffies - ar->ps.last_slept));
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(sta_psm, 160);
+
+static char *carl9170_debugfs_tx_stuck_read(struct ar9170 *ar, char *buf,
+                                           size_t bufsize, ssize_t *len)
+{
+       int i;
+
+       for (i = 0; i < ar->hw->queues; i++) {
+               ADD(buf, *len, bufsize, "TX queue [%d]: %10d max:%10d ms.\n",
+                   i, ieee80211_queue_stopped(ar->hw, i) ?
+                   jiffies_to_msecs(jiffies - ar->queue_stop_timeout[i]) : 0,
+                   jiffies_to_msecs(ar->max_queue_stop_timeout[i]));
+
+               ar->max_queue_stop_timeout[i] = 0;
+       }
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(tx_stuck, 180);
+
+static char *carl9170_debugfs_phy_noise_read(struct ar9170 *ar, char *buf,
+                                            size_t bufsize, ssize_t *len)
+{
+       int err;
+
+       err = carl9170_get_noisefloor(ar);
+       if (err) {
+               *len = err;
+               return buf;
+       }
+
+       ADD(buf, *len, bufsize, "Chain 0: %10d dBm, ext. chan.:%10d dBm\n",
+           ar->noise[0], ar->noise[2]);
+       ADD(buf, *len, bufsize, "Chain 2: %10d dBm, ext. chan.:%10d dBm\n",
+           ar->noise[1], ar->noise[3]);
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(phy_noise, 180);
+
+static char *carl9170_debugfs_vif_dump_read(struct ar9170 *ar, char *buf,
+                                           size_t bufsize, ssize_t *len)
+{
+       struct carl9170_vif_info *iter;
+       int i = 0;
+
+       ADD(buf, *len, bufsize, "registered VIFs:%d \\ %d\n",
+           ar->vifs, ar->fw.vif_num);
+
+       ADD(buf, *len, bufsize, "VIF bitmap: [");
+
+       *len += bitmap_scnprintf(&buf[*len], bufsize - *len,
+                                &ar->vif_bitmap, ar->fw.vif_num);
+
+       ADD(buf, *len, bufsize, "]\n");
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(iter, &ar->vif_list, list) {
+               struct ieee80211_vif *vif = carl9170_get_vif(iter);
+               ADD(buf, *len, bufsize, "\t%d = [%s VIF, id:%d, type:%x "
+                   " mac:%pM %s]\n", i, (carl9170_get_main_vif(ar) == vif ?
+                   "Master" : " Slave"), iter->id, vif->type, vif->addr,
+                   iter->enable_beacon ? "beaconing " : "");
+               i++;
+       }
+       rcu_read_unlock();
+
+       return buf;
+}
+DEBUGFS_DECLARE_RO_FILE(vif_dump, 8000);
+
+#define UPDATE_COUNTER(ar, name)       ({                              \
+       u32 __tmp[ARRAY_SIZE(name##_regs)];                             \
+       unsigned int __i, __err = -ENODEV;                              \
+                                                                       \
+       for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) {           \
+               __tmp[__i] = name##_regs[__i].reg;                      \
+               ar->debug.stats.name##_counter[__i] = 0;                \
+       }                                                               \
+                                                                       \
+       if (IS_STARTED(ar))                                             \
+               __err = carl9170_read_mreg(ar, ARRAY_SIZE(name##_regs), \
+                       __tmp, ar->debug.stats.name##_counter);         \
+       (__err); })
+
+#define TALLY_SUM_UP(ar, name) do {                                    \
+       unsigned int __i;                                               \
+                                                                       \
+       for (__i = 0; __i < ARRAY_SIZE(name##_regs); __i++) {           \
+               ar->debug.stats.name##_sum[__i] +=                      \
+                       ar->debug.stats.name##_counter[__i];            \
+       }                                                               \
+} while (0)
+
+#define DEBUGFS_HW_TALLY_FILE(name, f)                                 \
+static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar,       \
+        char *dum, size_t bufsize, ssize_t *ret)                       \
+{                                                                      \
+       char *buf;                                                      \
+       int i, max_len, err;                                            \
+                                                                       \
+       max_len = ARRAY_SIZE(name##_regs) * 80;                         \
+       buf = vmalloc(max_len);                                         \
+       if (!buf)                                                       \
+               return NULL;                                            \
+                                                                       \
+       err = UPDATE_COUNTER(ar, name);                                 \
+       if (err) {                                                      \
+               *ret = err;                                             \
+               return buf;                                             \
+       }                                                               \
+                                                                       \
+       TALLY_SUM_UP(ar, name);                                         \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(name##_regs); i++) {                 \
+               ADD(buf, *ret, max_len, "%22s = %" f "[+%" f "]\n",     \
+                   name##_regs[i].nreg, ar->debug.stats.name ##_sum[i],\
+                   ar->debug.stats.name ##_counter[i]);                \
+       }                                                               \
+                                                                       \
+       return buf;                                                     \
+}                                                                      \
+DEBUGFS_DECLARE_RO_FILE(name, 0);
+
+#define DEBUGFS_HW_REG_FILE(name, f)                                   \
+static char *carl9170_debugfs_##name ## _read(struct ar9170 *ar,       \
+       char *dum, size_t bufsize, ssize_t *ret)                        \
+{                                                                      \
+       char *buf;                                                      \
+       int i, max_len, err;                                            \
+                                                                       \
+       max_len = ARRAY_SIZE(name##_regs) * 80;                         \
+       buf = vmalloc(max_len);                                         \
+       if (!buf)                                                       \
+               return NULL;                                            \
+                                                                       \
+       err = UPDATE_COUNTER(ar, name);                                 \
+       if (err) {                                                      \
+               *ret = err;                                             \
+               return buf;                                             \
+       }                                                               \
+                                                                       \
+       for (i = 0; i < ARRAY_SIZE(name##_regs); i++) {                 \
+               ADD(buf, *ret, max_len, "%22s = %" f "\n",              \
+                   name##_regs[i].nreg,                                \
+                   ar->debug.stats.name##_counter[i]);                 \
+       }                                                               \
+                                                                       \
+       return buf;                                                     \
+}                                                                      \
+DEBUGFS_DECLARE_RO_FILE(name, 0);
+
+static ssize_t carl9170_debugfs_hw_ioread32_write(struct ar9170 *ar,
+       const char *buf, size_t count)
+{
+       int err = 0, i, n = 0, max_len = 32, res;
+       unsigned int reg, tmp;
+
+       if (!count)
+               return 0;
+
+       if (count > max_len)
+               return -E2BIG;
+
+       res = sscanf(buf, "0x%X %d", &reg, &n);
+       if (res < 1) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (res == 1)
+               n = 1;
+
+       if (n > 15) {
+               err = -EMSGSIZE;
+               goto out;
+       }
+
+       if ((reg >= 0x280000) || ((reg + (n << 2)) >= 0x280000)) {
+               err = -EADDRNOTAVAIL;
+               goto out;
+       }
+
+       if (reg & 3) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < n; i++) {
+               err = carl9170_read_reg(ar, reg + (i << 2), &tmp);
+               if (err)
+                       goto out;
+
+               ar->debug.ring[ar->debug.ring_tail].reg = reg + (i << 2);
+               ar->debug.ring[ar->debug.ring_tail].value = tmp;
+               ar->debug.ring_tail++;
+               ar->debug.ring_tail %= CARL9170_DEBUG_RING_SIZE;
+       }
+
+out:
+       return err ? err : count;
+}
+
+static char *carl9170_debugfs_hw_ioread32_read(struct ar9170 *ar, char *buf,
+                                              size_t bufsize, ssize_t *ret)
+{
+       int i = 0;
+
+       while (ar->debug.ring_head != ar->debug.ring_tail) {
+               ADD(buf, *ret, bufsize, "%.8x = %.8x\n",
+                   ar->debug.ring[ar->debug.ring_head].reg,
+                   ar->debug.ring[ar->debug.ring_head].value);
+
+               ar->debug.ring_head++;
+               ar->debug.ring_head %= CARL9170_DEBUG_RING_SIZE;
+
+               if (i++ == 64)
+                       break;
+       }
+       ar->debug.ring_head = ar->debug.ring_tail;
+       return buf;
+}
+DEBUGFS_DECLARE_RW_FILE(hw_ioread32, CARL9170_DEBUG_RING_SIZE * 40);
+
+static ssize_t carl9170_debugfs_bug_write(struct ar9170 *ar, const char *buf,
+                                         size_t count)
+{
+       int err;
+
+       if (count < 1)
+               return -EINVAL;
+
+       switch (buf[0]) {
+       case 'F':
+               ar->needs_full_reset = true;
+               break;
+
+       case 'R':
+               if (!IS_STARTED(ar)) {
+                       err = -EAGAIN;
+                       goto out;
+               }
+
+               ar->needs_full_reset = false;
+               break;
+
+       case 'M':
+               err = carl9170_mac_reset(ar);
+               if (err < 0)
+                       count = err;
+
+               goto out;
+
+       case 'P':
+               err = carl9170_set_channel(ar, ar->hw->conf.channel,
+                       ar->hw->conf.channel_type, CARL9170_RFI_COLD);
+               if (err < 0)
+                       count = err;
+
+               goto out;
+
+       default:
+               return -EINVAL;
+       }
+
+       carl9170_restart(ar, CARL9170_RR_USER_REQUEST);
+
+out:
+       return count;
+}
+
+static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf,
+                                      size_t bufsize, ssize_t *ret)
+{
+       ADD(buf, *ret, bufsize, "[P]hy reinit, [R]estart, [F]ull usb reset, "
+           "[M]ac reset\n");
+       ADD(buf, *ret, bufsize, "firmware restarts:%d, last reason:%d\n",
+               ar->restart_counter, ar->last_reason);
+       ADD(buf, *ret, bufsize, "phy reinit errors:%d (%d)\n",
+               ar->total_chan_fail, ar->chan_fail);
+       ADD(buf, *ret, bufsize, "reported firmware errors:%d\n",
+               ar->fw.err_counter);
+       ADD(buf, *ret, bufsize, "reported firmware BUGs:%d\n",
+               ar->fw.bug_counter);
+       ADD(buf, *ret, bufsize, "pending restart requests:%d\n",
+               atomic_read(&ar->pending_restarts));
+       return buf;
+}
+__DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED);
+
+static const char *erp_modes[] = {
+       [CARL9170_ERP_INVALID] = "INVALID",
+       [CARL9170_ERP_AUTO] = "Automatic",
+       [CARL9170_ERP_MAC80211] = "Set by MAC80211",
+       [CARL9170_ERP_OFF] = "Force Off",
+       [CARL9170_ERP_RTS] = "Force RTS",
+       [CARL9170_ERP_CTS] = "Force CTS"
+};
+
+static char *carl9170_debugfs_erp_read(struct ar9170 *ar, char *buf,
+                                      size_t bufsize, ssize_t *ret)
+{
+       ADD(buf, *ret, bufsize, "ERP Setting: (%d) -> %s\n", ar->erp_mode,
+           erp_modes[ar->erp_mode]);
+       return buf;
+}
+
+static ssize_t carl9170_debugfs_erp_write(struct ar9170 *ar, const char *buf,
+                                         size_t count)
+{
+       int res, val;
+
+       if (count < 1)
+               return -EINVAL;
+
+       res = sscanf(buf, "%d", &val);
+       if (res != 1)
+               return -EINVAL;
+
+       if (!((val > CARL9170_ERP_INVALID) &&
+             (val < __CARL9170_ERP_NUM)))
+               return -EINVAL;
+
+       ar->erp_mode = val;
+       return count;
+}
+
+DEBUGFS_DECLARE_RW_FILE(erp, 80);
+
+static ssize_t carl9170_debugfs_hw_iowrite32_write(struct ar9170 *ar,
+       const char *buf, size_t count)
+{
+       int err = 0, max_len = 22, res;
+       u32 reg, val;
+
+       if (!count)
+               return 0;
+
+       if (count > max_len)
+               return -E2BIG;
+
+       res = sscanf(buf, "0x%X 0x%X", &reg, &val);
+       if (res != 2) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (reg <= 0x100000 || reg >= 0x280000) {
+               err = -EADDRNOTAVAIL;
+               goto out;
+       }
+
+       if (reg & 3) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = carl9170_write_reg(ar, reg, val);
+       if (err)
+               goto out;
+
+out:
+       return err ? err : count;
+}
+DEBUGFS_DECLARE_WO_FILE(hw_iowrite32);
+
+DEBUGFS_HW_TALLY_FILE(hw_tx_tally, "u");
+DEBUGFS_HW_TALLY_FILE(hw_rx_tally, "u");
+DEBUGFS_HW_TALLY_FILE(hw_phy_errors, "u");
+DEBUGFS_HW_REG_FILE(hw_wlan_queue, ".8x");
+DEBUGFS_HW_REG_FILE(hw_pta_queue, ".8x");
+DEBUGFS_HW_REG_FILE(hw_ampdu_info, ".8x");
+DEBUGFS_QUEUE_DUMP(tx_status, 0);
+DEBUGFS_QUEUE_DUMP(tx_status, 1);
+DEBUGFS_QUEUE_DUMP(tx_status, 2);
+DEBUGFS_QUEUE_DUMP(tx_status, 3);
+DEBUGFS_QUEUE_DUMP(tx_pending, 0);
+DEBUGFS_QUEUE_DUMP(tx_pending, 1);
+DEBUGFS_QUEUE_DUMP(tx_pending, 2);
+DEBUGFS_QUEUE_DUMP(tx_pending, 3);
+DEBUGFS_READONLY_FILE(usb_tx_anch_urbs, 20, "%d",
+                     atomic_read(&ar->tx_anch_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_anch_urbs, 20, "%d",
+                     atomic_read(&ar->rx_anch_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_work_urbs, 20, "%d",
+                     atomic_read(&ar->rx_work_urbs));
+DEBUGFS_READONLY_FILE(usb_rx_pool_urbs, 20, "%d",
+                     atomic_read(&ar->rx_pool_urbs));
+
+DEBUGFS_READONLY_FILE(tx_total_queued, 20, "%d",
+                     atomic_read(&ar->tx_total_queued));
+DEBUGFS_READONLY_FILE(tx_ampdu_scheduler, 20, "%d",
+                     atomic_read(&ar->tx_ampdu_scheduler));
+DEBUGFS_READONLY_FILE(tx_ampdu_timeout, 20, "%d",
+                     ar->tx_ampdu_timeout);
+
+DEBUGFS_READONLY_FILE(tx_total_pending, 20, "%d",
+                     atomic_read(&ar->tx_total_pending));
+
+DEBUGFS_READONLY_FILE(tx_ampdu_list_len, 20, "%d",
+                     ar->tx_ampdu_list_len);
+
+DEBUGFS_READONLY_FILE(tx_ampdu_upload, 20, "%d",
+                     atomic_read(&ar->tx_ampdu_upload));
+
+DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago",
+       jiffies_to_msecs(jiffies - ar->tx_janitor_last_run));
+
+DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped);
+
+DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped);
+
+DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled);
+DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d",
+                     ar->rx_software_decryption);
+DEBUGFS_READONLY_FILE(ampdu_factor, 20, "%d",
+                     ar->current_factor);
+DEBUGFS_READONLY_FILE(ampdu_density, 20, "%d",
+                     ar->current_density);
+
+DEBUGFS_READONLY_FILE(beacon_int, 20, "%d TU", ar->global_beacon_int);
+DEBUGFS_READONLY_FILE(pretbtt, 20, "%d TU", ar->global_pretbtt);
+
+void carl9170_debugfs_register(struct ar9170 *ar)
+{
+       ar->debug_dir = debugfs_create_dir(KBUILD_MODNAME,
+               ar->hw->wiphy->debugfsdir);
+
+#define DEBUGFS_ADD(name)                                              \
+       debugfs_create_file(#name, carl_debugfs_##name ##_ops.attr,     \
+                           ar->debug_dir, ar,                          \
+                           &carl_debugfs_##name ## _ops.fops);
+
+       DEBUGFS_ADD(usb_tx_anch_urbs);
+       DEBUGFS_ADD(usb_rx_pool_urbs);
+       DEBUGFS_ADD(usb_rx_anch_urbs);
+       DEBUGFS_ADD(usb_rx_work_urbs);
+
+       DEBUGFS_ADD(tx_total_queued);
+       DEBUGFS_ADD(tx_total_pending);
+       DEBUGFS_ADD(tx_dropped);
+       DEBUGFS_ADD(tx_stuck);
+       DEBUGFS_ADD(tx_ampdu_upload);
+       DEBUGFS_ADD(tx_ampdu_scheduler);
+       DEBUGFS_ADD(tx_ampdu_list_len);
+
+       DEBUGFS_ADD(rx_dropped);
+       DEBUGFS_ADD(sniffer_enabled);
+       DEBUGFS_ADD(rx_software_decryption);
+
+       DEBUGFS_ADD(mem_usage);
+       DEBUGFS_ADD(qos_stat);
+       DEBUGFS_ADD(sta_psm);
+       DEBUGFS_ADD(ampdu_state);
+
+       DEBUGFS_ADD(hw_tx_tally);
+       DEBUGFS_ADD(hw_rx_tally);
+       DEBUGFS_ADD(hw_phy_errors);
+       DEBUGFS_ADD(phy_noise);
+
+       DEBUGFS_ADD(hw_wlan_queue);
+       DEBUGFS_ADD(hw_pta_queue);
+       DEBUGFS_ADD(hw_ampdu_info);
+
+       DEBUGFS_ADD(ampdu_density);
+       DEBUGFS_ADD(ampdu_factor);
+
+       DEBUGFS_ADD(tx_ampdu_timeout);
+
+       DEBUGFS_ADD(tx_janitor_last_run);
+
+       DEBUGFS_ADD(tx_status_0);
+       DEBUGFS_ADD(tx_status_1);
+       DEBUGFS_ADD(tx_status_2);
+       DEBUGFS_ADD(tx_status_3);
+
+       DEBUGFS_ADD(tx_pending_0);
+       DEBUGFS_ADD(tx_pending_1);
+       DEBUGFS_ADD(tx_pending_2);
+       DEBUGFS_ADD(tx_pending_3);
+
+       DEBUGFS_ADD(hw_ioread32);
+       DEBUGFS_ADD(hw_iowrite32);
+       DEBUGFS_ADD(bug);
+
+       DEBUGFS_ADD(erp);
+
+       DEBUGFS_ADD(vif_dump);
+
+       DEBUGFS_ADD(beacon_int);
+       DEBUGFS_ADD(pretbtt);
+
+#undef DEBUGFS_ADD
+}
+
+void carl9170_debugfs_unregister(struct ar9170 *ar)
+{
+       debugfs_remove_recursive(ar->debug_dir);
+}
diff --git a/drivers/net/wireless/ath/carl9170/debug.h b/drivers/net/wireless/ath/carl9170/debug.h
new file mode 100644 (file)
index 0000000..ea4b975
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * debug header
+ *
+ * Copyright 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __DEBUG_H
+#define __DEBUG_H
+
+#include "eeprom.h"
+#include "wlan.h"
+#include "hw.h"
+#include "fwdesc.h"
+#include "fwcmd.h"
+#include "../regd.h"
+
+struct hw_stat_reg_entry {
+       u32 reg;
+       char nreg[32];
+};
+
+#define        STAT_MAC_REG(reg)       \
+       { (AR9170_MAC_REG_##reg), #reg }
+
+#define        STAT_PTA_REG(reg)       \
+       { (AR9170_PTA_REG_##reg), #reg }
+
+#define        STAT_USB_REG(reg)       \
+       { (AR9170_USB_REG_##reg), #reg }
+
+static const struct hw_stat_reg_entry hw_rx_tally_regs[] = {
+       STAT_MAC_REG(RX_CRC32),         STAT_MAC_REG(RX_CRC16),
+       STAT_MAC_REG(RX_TIMEOUT_COUNT), STAT_MAC_REG(RX_ERR_DECRYPTION_UNI),
+       STAT_MAC_REG(RX_ERR_DECRYPTION_MUL), STAT_MAC_REG(RX_MPDU),
+       STAT_MAC_REG(RX_DROPPED_MPDU),  STAT_MAC_REG(RX_DEL_MPDU),
+};
+
+static const struct hw_stat_reg_entry hw_phy_errors_regs[] = {
+       STAT_MAC_REG(RX_PHY_MISC_ERROR), STAT_MAC_REG(RX_PHY_XR_ERROR),
+       STAT_MAC_REG(RX_PHY_OFDM_ERROR), STAT_MAC_REG(RX_PHY_CCK_ERROR),
+       STAT_MAC_REG(RX_PHY_HT_ERROR), STAT_MAC_REG(RX_PHY_TOTAL),
+};
+
+static const struct hw_stat_reg_entry hw_tx_tally_regs[] = {
+       STAT_MAC_REG(TX_TOTAL),         STAT_MAC_REG(TX_UNDERRUN),
+       STAT_MAC_REG(TX_RETRY),
+};
+
+static const struct hw_stat_reg_entry hw_wlan_queue_regs[] = {
+       STAT_MAC_REG(DMA_STATUS),       STAT_MAC_REG(DMA_TRIGGER),
+       STAT_MAC_REG(DMA_TXQ0_ADDR),    STAT_MAC_REG(DMA_TXQ0_CURR_ADDR),
+       STAT_MAC_REG(DMA_TXQ1_ADDR),    STAT_MAC_REG(DMA_TXQ1_CURR_ADDR),
+       STAT_MAC_REG(DMA_TXQ2_ADDR),    STAT_MAC_REG(DMA_TXQ2_CURR_ADDR),
+       STAT_MAC_REG(DMA_TXQ3_ADDR),    STAT_MAC_REG(DMA_TXQ3_CURR_ADDR),
+       STAT_MAC_REG(DMA_RXQ_ADDR),     STAT_MAC_REG(DMA_RXQ_CURR_ADDR),
+};
+
+static const struct hw_stat_reg_entry hw_ampdu_info_regs[] = {
+       STAT_MAC_REG(AMPDU_DENSITY),    STAT_MAC_REG(AMPDU_FACTOR),
+};
+
+static const struct hw_stat_reg_entry hw_pta_queue_regs[] = {
+       STAT_PTA_REG(DN_CURR_ADDRH),    STAT_PTA_REG(DN_CURR_ADDRL),
+       STAT_PTA_REG(UP_CURR_ADDRH),    STAT_PTA_REG(UP_CURR_ADDRL),
+       STAT_PTA_REG(DMA_STATUS),       STAT_PTA_REG(DMA_MODE_CTRL),
+};
+
+#define        DEFINE_TALLY(name)                                      \
+       u32 name##_sum[ARRAY_SIZE(name##_regs)],                \
+           name##_counter[ARRAY_SIZE(name##_regs)]             \
+
+#define        DEFINE_STAT(name)                                       \
+       u32 name##_counter[ARRAY_SIZE(name##_regs)]             \
+
+struct ath_stats {
+       DEFINE_TALLY(hw_tx_tally);
+       DEFINE_TALLY(hw_rx_tally);
+       DEFINE_TALLY(hw_phy_errors);
+       DEFINE_STAT(hw_wlan_queue);
+       DEFINE_STAT(hw_pta_queue);
+       DEFINE_STAT(hw_ampdu_info);
+};
+
+struct carl9170_debug_mem_rbe {
+       u32 reg;
+       u32 value;
+};
+
+#define        CARL9170_DEBUG_RING_SIZE                        64
+
+struct carl9170_debug {
+       struct ath_stats stats;
+       struct carl9170_debug_mem_rbe ring[CARL9170_DEBUG_RING_SIZE];
+       struct mutex ring_lock;
+       unsigned int ring_head, ring_tail;
+       struct delayed_work update_tally;
+};
+
+struct ar9170;
+
+void carl9170_debugfs_register(struct ar9170 *ar);
+void carl9170_debugfs_unregister(struct ar9170 *ar);
+#endif /* __DEBUG_H */
diff --git a/drivers/net/wireless/ath/carl9170/eeprom.h b/drivers/net/wireless/ath/carl9170/eeprom.h
new file mode 100644 (file)
index 0000000..7cff40a
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * EEPROM layout
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CARL9170_SHARED_EEPROM_H
+#define __CARL9170_SHARED_EEPROM_H
+
+#define AR9170_EEPROM_START            0x1600
+
+#define AR5416_MAX_CHAINS              2
+#define AR5416_MODAL_SPURS             5
+
+struct ar9170_eeprom_modal {
+       __le32  antCtrlChain[AR5416_MAX_CHAINS];
+       __le32  antCtrlCommon;
+       s8      antennaGainCh[AR5416_MAX_CHAINS];
+       u8      switchSettling;
+       u8      txRxAttenCh[AR5416_MAX_CHAINS];
+       u8      rxTxMarginCh[AR5416_MAX_CHAINS];
+       s8      adcDesiredSize;
+       s8      pgaDesiredSize;
+       u8      xlnaGainCh[AR5416_MAX_CHAINS];
+       u8      txEndToXpaOff;
+       u8      txEndToRxOn;
+       u8      txFrameToXpaOn;
+       u8      thresh62;
+       s8      noiseFloorThreshCh[AR5416_MAX_CHAINS];
+       u8      xpdGain;
+       u8      xpd;
+       s8      iqCalICh[AR5416_MAX_CHAINS];
+       s8      iqCalQCh[AR5416_MAX_CHAINS];
+       u8      pdGainOverlap;
+       u8      ob;
+       u8      db;
+       u8      xpaBiasLvl;
+       u8      pwrDecreaseFor2Chain;
+       u8      pwrDecreaseFor3Chain;
+       u8      txFrameToDataStart;
+       u8      txFrameToPaOn;
+       u8      ht40PowerIncForPdadc;
+       u8      bswAtten[AR5416_MAX_CHAINS];
+       u8      bswMargin[AR5416_MAX_CHAINS];
+       u8      swSettleHt40;
+       u8      reserved[22];
+       struct spur_channel {
+               __le16 spurChan;
+               u8      spurRangeLow;
+               u8      spurRangeHigh;
+       } __packed spur_channels[AR5416_MODAL_SPURS];
+} __packed;
+
+#define AR5416_NUM_PD_GAINS            4
+#define AR5416_PD_GAIN_ICEPTS          5
+
+struct ar9170_calibration_data_per_freq {
+       u8      pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+       u8      vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+} __packed;
+
+#define AR5416_NUM_5G_CAL_PIERS                8
+#define AR5416_NUM_2G_CAL_PIERS                4
+
+#define AR5416_NUM_5G_TARGET_PWRS      8
+#define AR5416_NUM_2G_CCK_TARGET_PWRS  3
+#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4
+#define AR5416_MAX_NUM_TGT_PWRS                8
+
+struct ar9170_calibration_target_power_legacy {
+       u8      freq;
+       u8      power[4];
+} __packed;
+
+struct ar9170_calibration_target_power_ht {
+       u8      freq;
+       u8      power[8];
+} __packed;
+
+#define AR5416_NUM_CTLS                        24
+
+struct ar9170_calctl_edges {
+       u8      channel;
+#define AR9170_CALCTL_EDGE_FLAGS       0xC0
+       u8      power_flags;
+} __packed;
+
+#define AR5416_NUM_BAND_EDGES          8
+
+struct ar9170_calctl_data {
+       struct ar9170_calctl_edges
+               control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+} __packed;
+
+struct ar9170_eeprom {
+       __le16  length;
+       __le16  checksum;
+       __le16  version;
+       u8      operating_flags;
+#define AR9170_OPFLAG_5GHZ             1
+#define AR9170_OPFLAG_2GHZ             2
+       u8      misc;
+       __le16  reg_domain[2];
+       u8      mac_address[6];
+       u8      rx_mask;
+       u8      tx_mask;
+       __le16  rf_silent;
+       __le16  bluetooth_options;
+       __le16  device_capabilities;
+       __le32  build_number;
+       u8      deviceType;
+       u8      reserved[33];
+
+       u8      customer_data[64];
+
+       struct ar9170_eeprom_modal
+               modal_header[2];
+
+       u8      cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
+       u8      cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
+
+       struct ar9170_calibration_data_per_freq
+               cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
+               cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
+
+       /* power calibration data */
+       struct ar9170_calibration_target_power_legacy
+               cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
+       struct ar9170_calibration_target_power_ht
+               cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
+               cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
+
+       struct ar9170_calibration_target_power_legacy
+               cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
+               cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+       struct ar9170_calibration_target_power_ht
+               cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
+               cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+
+       /* conformance testing limits */
+       u8      ctl_index[AR5416_NUM_CTLS];
+       struct ar9170_calctl_data
+               ctl_data[AR5416_NUM_CTLS];
+
+       u8      pad;
+       __le16  subsystem_id;
+} __packed;
+
+#define AR9170_LED_MODE_POWER_ON               0x0001
+#define AR9170_LED_MODE_RESERVED               0x0002
+#define AR9170_LED_MODE_DISABLE_STATE          0x0004
+#define AR9170_LED_MODE_OFF_IN_PSM             0x0008
+
+/* AR9170_LED_MODE BIT is set */
+#define AR9170_LED_MODE_FREQUENCY_S            4
+#define AR9170_LED_MODE_FREQUENCY              0x0030
+#define AR9170_LED_MODE_FREQUENCY_1HZ          0x0000
+#define AR9170_LED_MODE_FREQUENCY_0_5HZ                0x0010
+#define AR9170_LED_MODE_FREQUENCY_0_25HZ       0x0020
+#define AR9170_LED_MODE_FREQUENCY_0_125HZ      0x0030
+
+/* AR9170_LED_MODE BIT is not set */
+#define AR9170_LED_MODE_CONN_STATE_S           4
+#define AR9170_LED_MODE_CONN_STATE             0x0030
+#define AR9170_LED_MODE_CONN_STATE_FORCE_OFF   0x0000
+#define AR9170_LED_MODE_CONN_STATE_FORCE_ON    0x0010
+/* Idle off / Active on */
+#define AR9170_LED_MODE_CONN_STATE_IOFF_AON    0x0020
+/* Idle on / Active off */
+#define AR9170_LED_MODE_CONN_STATE_ION_AOFF    0x0010
+
+#define AR9170_LED_MODE_MODE                   0x0040
+#define AR9170_LED_MODE_RESERVED2              0x0080
+
+#define AR9170_LED_MODE_TON_SCAN_S             8
+#define AR9170_LED_MODE_TON_SCAN               0x0f00
+
+#define AR9170_LED_MODE_TOFF_SCAN_S            12
+#define AR9170_LED_MODE_TOFF_SCAN              0xf000
+
+struct ar9170_led_mode {
+       __le16 led;
+};
+
+#endif /* __CARL9170_SHARED_EEPROM_H */
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
new file mode 100644 (file)
index 0000000..3661546
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * firmware parser
+ *
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include "carl9170.h"
+#include "fwcmd.h"
+#include "version.h"
+
+#define MAKE_STR(symbol) #symbol
+#define TO_STR(symbol) MAKE_STR(symbol)
+#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER)
+MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT);
+
+static const u8 otus_magic[4] = { OTUS_MAGIC };
+
+static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
+       const unsigned int len, const u8 compatible_revision)
+{
+       const struct carl9170fw_desc_head *iter;
+
+       carl9170fw_for_each_hdr(iter, ar->fw.desc) {
+               if (carl9170fw_desc_cmp(iter, descid, len,
+                                       compatible_revision))
+                       return (void *)iter;
+       }
+
+       /* needed to find the LAST desc */
+       if (carl9170fw_desc_cmp(iter, descid, len,
+                               compatible_revision))
+               return (void *)iter;
+
+       return NULL;
+}
+
+static int carl9170_fw_verify_descs(struct ar9170 *ar,
+       const struct carl9170fw_desc_head *head, unsigned int max_len)
+{
+       const struct carl9170fw_desc_head *pos;
+       unsigned long pos_addr, end_addr;
+       unsigned int pos_length;
+
+       if (max_len < sizeof(*pos))
+               return -ENODATA;
+
+       max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);
+
+       pos = head;
+       pos_addr = (unsigned long) pos;
+       end_addr = pos_addr + max_len;
+
+       while (pos_addr < end_addr) {
+               if (pos_addr + sizeof(*head) > end_addr)
+                       return -E2BIG;
+
+               pos_length = le16_to_cpu(pos->length);
+
+               if (pos_length < sizeof(*head))
+                       return -EBADMSG;
+
+               if (pos_length > max_len)
+                       return -EOVERFLOW;
+
+               if (pos_addr + pos_length > end_addr)
+                       return -EMSGSIZE;
+
+               if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
+                                       CARL9170FW_LAST_DESC_SIZE,
+                                       CARL9170FW_LAST_DESC_CUR_VER))
+                       return 0;
+
+               pos_addr += pos_length;
+               pos = (void *)pos_addr;
+               max_len -= pos_length;
+       }
+       return -EINVAL;
+}
+
+static void carl9170_fw_info(struct ar9170 *ar)
+{
+       const struct carl9170fw_motd_desc *motd_desc;
+       unsigned int str_ver_len;
+       u32 fw_date;
+
+       dev_info(&ar->udev->dev, "driver   API: %s 2%03d-%02d-%02d [%d-%d]\n",
+               CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
+               CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
+               CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);
+
+       motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
+               sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);
+
+       if (motd_desc) {
+               str_ver_len = strnlen(motd_desc->release,
+                       CARL9170FW_MOTD_RELEASE_LEN);
+
+               fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
+
+               dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
+                        str_ver_len, motd_desc->release,
+                        CARL9170FW_GET_YEAR(fw_date),
+                        CARL9170FW_GET_MONTH(fw_date),
+                        CARL9170FW_GET_DAY(fw_date));
+
+               strlcpy(ar->hw->wiphy->fw_version, motd_desc->release,
+                       sizeof(ar->hw->wiphy->fw_version));
+       }
+}
+
+static bool valid_dma_addr(const u32 address)
+{
+       if (address >= AR9170_SRAM_OFFSET &&
+           address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
+               return true;
+
+       return false;
+}
+
+static bool valid_cpu_addr(const u32 address)
+{
+       if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
+           address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
+               return true;
+
+       return false;
+}
+
+static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
+{
+       const struct carl9170fw_otus_desc *otus_desc;
+       const struct carl9170fw_chk_desc *chk_desc;
+       const struct carl9170fw_last_desc *last_desc;
+
+       last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,
+               sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER);
+       if (!last_desc)
+               return -EINVAL;
+
+       otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
+               sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
+       if (!otus_desc) {
+               dev_err(&ar->udev->dev, "failed to find compatible firmware "
+                       "descriptor.\n");
+               return -ENODATA;
+       }
+
+       chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
+               sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);
+
+       if (chk_desc) {
+               unsigned long fin, diff;
+               unsigned int dsc_len;
+               u32 crc32;
+
+               dsc_len = min_t(unsigned int, len,
+                       (unsigned long)chk_desc - (unsigned long)otus_desc);
+
+               fin = (unsigned long) last_desc + sizeof(*last_desc);
+               diff = fin - (unsigned long) otus_desc;
+
+               if (diff < len)
+                       len -= diff;
+
+               if (len < 256)
+                       return -EIO;
+
+               crc32 = crc32_le(~0, data, len);
+               if (cpu_to_le32(crc32) != chk_desc->fw_crc32) {
+                       dev_err(&ar->udev->dev, "fw checksum test failed.\n");
+                       return -ENOEXEC;
+               }
+
+               crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len);
+               if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) {
+                       dev_err(&ar->udev->dev, "descriptor check failed.\n");
+                       return -EINVAL;
+               }
+       } else {
+               dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
+       }
+
+#define SUPP(feat)                                             \
+       (carl9170fw_supports(otus_desc->feature_set, feat))
+
+       if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
+               dev_err(&ar->udev->dev, "invalid firmware descriptor "
+                       "format detected.\n");
+               return -EINVAL;
+       }
+
+       ar->fw.api_version = otus_desc->api_ver;
+
+       if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
+           ar->fw.api_version > CARL9170FW_API_MAX_VER) {
+               dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
+               return -EINVAL;
+       }
+
+       if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
+           !SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
+               dev_err(&ar->udev->dev, "firmware does support "
+                       "mandatory features.\n");
+               return -ECANCELED;
+       }
+
+       if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
+               __CARL9170FW_FEATURE_NUM) {
+               dev_warn(&ar->udev->dev, "driver does not support all "
+                        "firmware features.\n");
+       }
+
+       if (!SUPP(CARL9170FW_COMMAND_CAM)) {
+               dev_info(&ar->udev->dev, "crypto offloading is disabled "
+                        "by firmware.\n");
+               ar->disable_offload = true;
+       }
+
+       if (SUPP(CARL9170FW_PSM))
+               ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
+
+       if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
+               dev_err(&ar->udev->dev, "firmware does not provide "
+                       "mandatory interfaces.\n");
+               return -EINVAL;
+       }
+
+       if (SUPP(CARL9170FW_MINIBOOT))
+               ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
+       else
+               ar->fw.offset = 0;
+
+       if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
+               ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
+               ar->fw.tx_stream = true;
+       }
+
+       if (SUPP(CARL9170FW_USB_UP_STREAM))
+               ar->fw.rx_stream = true;
+
+       ar->fw.vif_num = otus_desc->vif_num;
+       ar->fw.cmd_bufs = otus_desc->cmd_bufs;
+       ar->fw.address = le32_to_cpu(otus_desc->fw_address);
+       ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
+       ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
+       atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
+       ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);
+
+       if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
+           ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
+           ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
+           ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
+           !valid_cpu_addr(ar->fw.address)) {
+               dev_err(&ar->udev->dev, "firmware shows obvious signs of "
+                       "malicious tampering.\n");
+               return -EINVAL;
+       }
+
+       ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
+       ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);
+
+       if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
+           AR9170_MAC_BCN_LENGTH_MAX) {
+               ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+
+               if (SUPP(CARL9170FW_WLANTX_CAB)) {
+                       ar->hw->wiphy->interface_modes |=
+                               BIT(NL80211_IFTYPE_AP);
+               }
+       }
+
+#undef SUPPORTED
+       return 0;
+}
+
+static struct carl9170fw_desc_head *
+carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)
+
+{
+       int scan = 0, found = 0;
+
+       if (!carl9170fw_size_check(len)) {
+               dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
+               return NULL;
+       }
+
+       while (scan < len - sizeof(struct carl9170fw_desc_head)) {
+               if (fw_data[scan++] == otus_magic[found])
+                       found++;
+               else
+                       found = 0;
+
+               if (scan >= len)
+                       break;
+
+               if (found == sizeof(otus_magic))
+                       break;
+       }
+
+       if (found != sizeof(otus_magic))
+               return NULL;
+
+       return (void *)&fw_data[scan - found];
+}
+
+int carl9170_fw_fix_eeprom(struct ar9170 *ar)
+{
+       const struct carl9170fw_fix_desc *fix_desc = NULL;
+       unsigned int i, n, off;
+       u32 *data = (void *)&ar->eeprom;
+
+       fix_desc = carl9170_fw_find_desc(ar, FIX_MAGIC,
+               sizeof(*fix_desc), CARL9170FW_FIX_DESC_CUR_VER);
+
+       if (!fix_desc)
+               return 0;
+
+       n = (le16_to_cpu(fix_desc->head.length) - sizeof(*fix_desc)) /
+           sizeof(struct carl9170fw_fix_entry);
+
+       for (i = 0; i < n; i++) {
+               off = le32_to_cpu(fix_desc->data[i].address) -
+                     AR9170_EEPROM_START;
+
+               if (off >= sizeof(struct ar9170_eeprom) || (off & 3)) {
+                       dev_err(&ar->udev->dev, "Skip invalid entry %d\n", i);
+                       continue;
+               }
+
+               data[off / sizeof(*data)] &=
+                       le32_to_cpu(fix_desc->data[i].mask);
+               data[off / sizeof(*data)] |=
+                       le32_to_cpu(fix_desc->data[i].value);
+       }
+
+       return 0;
+}
+
+int carl9170_parse_firmware(struct ar9170 *ar)
+{
+       const struct carl9170fw_desc_head *fw_desc = NULL;
+       const struct firmware *fw = ar->fw.fw;
+       unsigned long header_offset = 0;
+       int err;
+
+       if (WARN_ON(!fw))
+               return -EINVAL;
+
+       fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);
+
+       if (!fw_desc) {
+               dev_err(&ar->udev->dev, "unsupported firmware.\n");
+               return -ENODATA;
+       }
+
+       header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;
+
+       err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
+       if (err) {
+               dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
+               return err;
+       }
+
+       ar->fw.desc = fw_desc;
+
+       carl9170_fw_info(ar);
+
+       err = carl9170_fw(ar, fw->data, fw->size);
+       if (err) {
+               dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h
new file mode 100644 (file)
index 0000000..d4a4e1d
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * Firmware command interface definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CARL9170_SHARED_FWCMD_H
+#define __CARL9170_SHARED_FWCMD_H
+
+#define        CARL9170_MAX_CMD_LEN            64
+#define        CARL9170_MAX_CMD_PAYLOAD_LEN    60
+
+#define CARL9170FW_API_MIN_VER         1
+#define CARL9170FW_API_MAX_VER         1
+
+enum carl9170_cmd_oids {
+       CARL9170_CMD_RREG               = 0x00,
+       CARL9170_CMD_WREG               = 0x01,
+       CARL9170_CMD_ECHO               = 0x02,
+       CARL9170_CMD_SWRST              = 0x03,
+       CARL9170_CMD_REBOOT             = 0x04,
+       CARL9170_CMD_BCN_CTRL           = 0x05,
+       CARL9170_CMD_READ_TSF           = 0x06,
+
+       /* CAM */
+       CARL9170_CMD_EKEY               = 0x10,
+       CARL9170_CMD_DKEY               = 0x11,
+
+       /* RF / PHY */
+       CARL9170_CMD_FREQUENCY          = 0x20,
+       CARL9170_CMD_RF_INIT            = 0x21,
+       CARL9170_CMD_SYNTH              = 0x22,
+       CARL9170_CMD_FREQ_START         = 0x23,
+       CARL9170_CMD_PSM                = 0x24,
+
+       /* Asychronous command flag */
+       CARL9170_CMD_ASYNC_FLAG         = 0x40,
+       CARL9170_CMD_WREG_ASYNC         = (CARL9170_CMD_WREG |
+                                          CARL9170_CMD_ASYNC_FLAG),
+       CARL9170_CMD_REBOOT_ASYNC       = (CARL9170_CMD_REBOOT |
+                                          CARL9170_CMD_ASYNC_FLAG),
+       CARL9170_CMD_BCN_CTRL_ASYNC     = (CARL9170_CMD_BCN_CTRL |
+                                          CARL9170_CMD_ASYNC_FLAG),
+       CARL9170_CMD_PSM_ASYNC          = (CARL9170_CMD_PSM |
+                                          CARL9170_CMD_ASYNC_FLAG),
+
+       /* responses and traps */
+       CARL9170_RSP_FLAG               = 0xc0,
+       CARL9170_RSP_PRETBTT            = 0xc0,
+       CARL9170_RSP_TXCOMP             = 0xc1,
+       CARL9170_RSP_BEACON_CONFIG      = 0xc2,
+       CARL9170_RSP_ATIM               = 0xc3,
+       CARL9170_RSP_WATCHDOG           = 0xc6,
+       CARL9170_RSP_TEXT               = 0xca,
+       CARL9170_RSP_HEXDUMP            = 0xcc,
+       CARL9170_RSP_RADAR              = 0xcd,
+       CARL9170_RSP_GPIO               = 0xce,
+       CARL9170_RSP_BOOT               = 0xcf,
+};
+
+struct carl9170_set_key_cmd {
+       __le16          user;
+       __le16          keyId;
+       __le16          type;
+       u8              macAddr[6];
+       u32             key[4];
+} __packed;
+#define CARL9170_SET_KEY_CMD_SIZE              28
+
+struct carl9170_disable_key_cmd {
+       __le16          user;
+       __le16          padding;
+} __packed;
+#define CARL9170_DISABLE_KEY_CMD_SIZE          4
+
+struct carl9170_u32_list {
+       u32     vals[0];
+} __packed;
+
+struct carl9170_reg_list {
+       __le32          regs[0];
+} __packed;
+
+struct carl9170_write_reg {
+       struct {
+               __le32          addr;
+               __le32          val;
+       } regs[0] __packed;
+} __packed;
+
+#define        CARL9170FW_PHY_HT_ENABLE                0x4
+#define        CARL9170FW_PHY_HT_DYN2040               0x8
+#define        CARL9170FW_PHY_HT_EXT_CHAN_OFF          0x3
+#define        CARL9170FW_PHY_HT_EXT_CHAN_OFF_S        2
+
+struct carl9170_rf_init {
+       __le32          freq;
+       u8              ht_settings;
+       u8              padding2[3];
+       __le32          delta_slope_coeff_exp;
+       __le32          delta_slope_coeff_man;
+       __le32          delta_slope_coeff_exp_shgi;
+       __le32          delta_slope_coeff_man_shgi;
+       __le32          finiteLoopCount;
+} __packed;
+#define CARL9170_RF_INIT_SIZE          28
+
+struct carl9170_rf_init_result {
+       __le32          ret;            /* AR9170_PHY_REG_AGC_CONTROL */
+} __packed;
+#define        CARL9170_RF_INIT_RESULT_SIZE    4
+
+#define        CARL9170_PSM_SLEEP              0x1000
+#define        CARL9170_PSM_SOFTWARE           0
+#define        CARL9170_PSM_WAKE               0 /* internally used. */
+#define        CARL9170_PSM_COUNTER            0xfff
+#define        CARL9170_PSM_COUNTER_S          0
+
+struct carl9170_psm {
+       __le32          state;
+} __packed;
+#define CARL9170_PSM_SIZE              4
+
+struct carl9170_bcn_ctrl_cmd {
+       __le32          vif_id;
+       __le32          mode;
+       __le32          bcn_addr;
+       __le32          bcn_len;
+} __packed;
+#define CARL9170_BCN_CTRL_CMD_SIZE     16
+
+#define CARL9170_BCN_CTRL_DRAIN        0
+#define CARL9170_BCN_CTRL_CAB_TRIGGER  1
+
+struct carl9170_cmd_head {
+       union {
+               struct {
+                       u8      len;
+                       u8      cmd;
+                       u8      seq;
+                       u8      ext;
+               } __packed;
+
+               u32 hdr_data;
+       } __packed;
+} __packed;
+
+struct carl9170_cmd {
+       struct carl9170_cmd_head hdr;
+       union {
+               struct carl9170_set_key_cmd     setkey;
+               struct carl9170_disable_key_cmd disablekey;
+               struct carl9170_u32_list        echo;
+               struct carl9170_reg_list        rreg;
+               struct carl9170_write_reg       wreg;
+               struct carl9170_rf_init         rf_init;
+               struct carl9170_psm             psm;
+               struct carl9170_bcn_ctrl_cmd    bcn_ctrl;
+               u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
+       } __packed;
+} __packed;
+
+#define        CARL9170_TX_STATUS_QUEUE        3
+#define        CARL9170_TX_STATUS_QUEUE_S      0
+#define        CARL9170_TX_STATUS_RIX_S        2
+#define        CARL9170_TX_STATUS_RIX          (3 << CARL9170_TX_STATUS_RIX_S)
+#define        CARL9170_TX_STATUS_TRIES_S      4
+#define        CARL9170_TX_STATUS_TRIES        (7 << CARL9170_TX_STATUS_TRIES_S)
+#define        CARL9170_TX_STATUS_SUCCESS      0x80
+
+/*
+ * NOTE:
+ * Both structs [carl9170_tx_status and _carl9170_tx_status]
+ * need to be "bit for bit" in sync.
+ */
+struct carl9170_tx_status {
+       /*
+        * Beware of compiler bugs in all gcc pre 4.4!
+        */
+
+       u8 cookie;
+       u8 queue:2;
+       u8 rix:2;
+       u8 tries:3;
+       u8 success:1;
+} __packed;
+struct _carl9170_tx_status {
+       /*
+        * This version should be immune to all alignment bugs.
+        */
+
+       u8 cookie;
+       u8 info;
+} __packed;
+#define CARL9170_TX_STATUS_SIZE                2
+
+#define        CARL9170_RSP_TX_STATUS_NUM      (CARL9170_MAX_CMD_PAYLOAD_LEN / \
+                                        sizeof(struct _carl9170_tx_status))
+
+#define        CARL9170_TX_MAX_RATE_TRIES      7
+
+#define        CARL9170_TX_MAX_RATES           4
+#define        CARL9170_TX_MAX_RETRY_RATES     (CARL9170_TX_MAX_RATES - 1)
+#define        CARL9170_ERR_MAGIC              "ERR:"
+#define        CARL9170_BUG_MAGIC              "BUG:"
+
+struct carl9170_gpio {
+       __le32 gpio;
+} __packed;
+#define CARL9170_GPIO_SIZE             4
+
+struct carl9170_tsf_rsp {
+       union {
+               __le32 tsf[2];
+               __le64 tsf_64;
+       } __packed;
+} __packed;
+#define CARL9170_TSF_RSP_SIZE          8
+
+struct carl9170_rsp {
+       struct carl9170_cmd_head hdr;
+
+       union {
+               struct carl9170_rf_init_result  rf_init_res;
+               struct carl9170_u32_list        rreg_res;
+               struct carl9170_u32_list        echo;
+               struct carl9170_tx_status       tx_status[0];
+               struct _carl9170_tx_status      _tx_status[0];
+               struct carl9170_gpio            gpio;
+               struct carl9170_tsf_rsp         tsf;
+               struct carl9170_psm             psm;
+               u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
+       } __packed;
+} __packed;
+
+#endif /* __CARL9170_SHARED_FWCMD_H */
diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h
new file mode 100644 (file)
index 0000000..7cd8117
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Shared CARL9170 Header
+ *
+ * Firmware descriptor format
+ *
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ */
+
+#ifndef __CARL9170_SHARED_FWDESC_H
+#define __CARL9170_SHARED_FWDESC_H
+
+/* NOTE: Don't mess with the order of the flags! */
+enum carl9170fw_feature_list {
+       /* Always set */
+       CARL9170FW_DUMMY_FEATURE,
+
+       /*
+        * Indicates that this image has special boot block which prevents
+        * legacy drivers to drive the firmware.
+        */
+       CARL9170FW_MINIBOOT,
+
+       /* usb registers are initialized by the firmware */
+       CARL9170FW_USB_INIT_FIRMWARE,
+
+       /* command traps & notifications are send through EP2 */
+       CARL9170FW_USB_RESP_EP2,
+
+       /* usb download (app -> fw) stream */
+       CARL9170FW_USB_DOWN_STREAM,
+
+       /* usb upload (fw -> app) stream */
+       CARL9170FW_USB_UP_STREAM,
+
+       /* unusable - reserved to flag non-functional debug firmwares */
+       CARL9170FW_UNUSABLE,
+
+       /* AR9170_CMD_RF_INIT, AR9170_CMD_FREQ_START, AR9170_CMD_FREQUENCY */
+       CARL9170FW_COMMAND_PHY,
+
+       /* AR9170_CMD_EKEY, AR9170_CMD_DKEY */
+       CARL9170FW_COMMAND_CAM,
+
+       /* Firmware has a software Content After Beacon Queueing mechanism */
+       CARL9170FW_WLANTX_CAB,
+
+       /* The firmware is capable of responding to incoming BAR frames */
+       CARL9170FW_HANDLE_BACK_REQ,
+
+       /* GPIO Interrupt | CARL9170_RSP_GPIO */
+       CARL9170FW_GPIO_INTERRUPT,
+
+       /* Firmware PSM support | CARL9170_CMD_PSM */
+       CARL9170FW_PSM,
+
+       /* KEEP LAST */
+       __CARL9170FW_FEATURE_NUM
+};
+
+#define OTUS_MAGIC     "OTAR"
+#define MOTD_MAGIC     "MOTD"
+#define FIX_MAGIC      "FIX\0"
+#define DBG_MAGIC      "DBG\0"
+#define CHK_MAGIC      "CHK\0"
+#define LAST_MAGIC     "LAST"
+
+#define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
+#define CARL9170FW_SET_MONTH(m) ((((m) - 1) % 12) * 31)
+#define CARL9170FW_SET_YEAR(y) (((y) - 10) * 372)
+
+#define CARL9170FW_GET_DAY(d) (((d) % 31) + 1)
+#define CARL9170FW_GET_MONTH(m) ((((m) / 31) % 12) + 1)
+#define CARL9170FW_GET_YEAR(y) ((y) / 372 + 10)
+
+struct carl9170fw_desc_head {
+       u8      magic[4];
+       __le16 length;
+       u8 min_ver;
+       u8 cur_ver;
+} __packed;
+#define CARL9170FW_DESC_HEAD_SIZE                      \
+       (sizeof(struct carl9170fw_desc_head))
+
+#define CARL9170FW_OTUS_DESC_MIN_VER           6
+#define CARL9170FW_OTUS_DESC_CUR_VER           6
+struct carl9170fw_otus_desc {
+       struct carl9170fw_desc_head head;
+       __le32 feature_set;
+       __le32 fw_address;
+       __le32 bcn_addr;
+       __le16 bcn_len;
+       __le16 miniboot_size;
+       __le16 tx_frag_len;
+       __le16 rx_max_frame_len;
+       u8 tx_descs;
+       u8 cmd_bufs;
+       u8 api_ver;
+       u8 vif_num;
+} __packed;
+#define CARL9170FW_OTUS_DESC_SIZE                      \
+       (sizeof(struct carl9170fw_otus_desc))
+
+#define CARL9170FW_MOTD_STRING_LEN                     24
+#define CARL9170FW_MOTD_RELEASE_LEN                    20
+#define CARL9170FW_MOTD_DESC_MIN_VER                   1
+#define CARL9170FW_MOTD_DESC_CUR_VER                   2
+struct carl9170fw_motd_desc {
+       struct carl9170fw_desc_head head;
+       __le32 fw_year_month_day;
+       char desc[CARL9170FW_MOTD_STRING_LEN];
+       char release[CARL9170FW_MOTD_RELEASE_LEN];
+} __packed;
+#define CARL9170FW_MOTD_DESC_SIZE                      \
+       (sizeof(struct carl9170fw_motd_desc))
+
+#define CARL9170FW_FIX_DESC_MIN_VER                    1
+#define CARL9170FW_FIX_DESC_CUR_VER                    2
+struct carl9170fw_fix_entry {
+       __le32 address;
+       __le32 mask;
+       __le32 value;
+} __packed;
+
+struct carl9170fw_fix_desc {
+       struct carl9170fw_desc_head head;
+       struct carl9170fw_fix_entry data[0];
+} __packed;
+#define CARL9170FW_FIX_DESC_SIZE                       \
+       (sizeof(struct carl9170fw_fix_desc))
+
+#define CARL9170FW_DBG_DESC_MIN_VER                    1
+#define CARL9170FW_DBG_DESC_CUR_VER                    2
+struct carl9170fw_dbg_desc {
+       struct carl9170fw_desc_head head;
+
+       __le32 bogoclock_addr;
+       __le32 counter_addr;
+       __le32 rx_total_addr;
+       __le32 rx_overrun_addr;
+
+       /* Put your debugging definitions here */
+} __packed;
+#define CARL9170FW_DBG_DESC_SIZE                       \
+       (sizeof(struct carl9170fw_dbg_desc))
+
+#define CARL9170FW_CHK_DESC_MIN_VER                    1
+#define CARL9170FW_CHK_DESC_CUR_VER                    2
+struct carl9170fw_chk_desc {
+       struct carl9170fw_desc_head head;
+       __le32 fw_crc32;
+       __le32 hdr_crc32;
+} __packed;
+#define CARL9170FW_CHK_DESC_SIZE                       \
+       (sizeof(struct carl9170fw_chk_desc))
+
+#define CARL9170FW_LAST_DESC_MIN_VER                   1
+#define CARL9170FW_LAST_DESC_CUR_VER                   2
+struct carl9170fw_last_desc {
+       struct carl9170fw_desc_head head;
+} __packed;
+#define CARL9170FW_LAST_DESC_SIZE                      \
+       (sizeof(struct carl9170fw_fix_desc))
+
+#define CARL9170FW_DESC_MAX_LENGTH                     8192
+
+#define CARL9170FW_FILL_DESC(_magic, _length, _min_ver, _cur_ver)      \
+       .head = {                                                       \
+               .magic = _magic,                                        \
+               .length = cpu_to_le16(_length),                         \
+               .min_ver = _min_ver,                                    \
+               .cur_ver = _cur_ver,                                    \
+       }
+
+static inline void carl9170fw_fill_desc(struct carl9170fw_desc_head *head,
+                                        u8 magic[4], __le16 length,
+                                        u8 min_ver, u8 cur_ver)
+{
+       head->magic[0] = magic[0];
+       head->magic[1] = magic[1];
+       head->magic[2] = magic[2];
+       head->magic[3] = magic[3];
+
+       head->length = length;
+       head->min_ver = min_ver;
+       head->cur_ver = cur_ver;
+}
+
+#define carl9170fw_for_each_hdr(desc, fw_desc)                         \
+       for (desc = fw_desc;                                            \
+            memcmp(desc->magic, LAST_MAGIC, 4) &&                      \
+            le16_to_cpu(desc->length) >= CARL9170FW_DESC_HEAD_SIZE &&  \
+            le16_to_cpu(desc->length) < CARL9170FW_DESC_MAX_LENGTH;    \
+            desc = (void *)((unsigned long)desc + le16_to_cpu(desc->length)))
+
+#define CHECK_HDR_VERSION(head, _min_ver)                              \
+       (((head)->cur_ver < _min_ver) || ((head)->min_ver > _min_ver))  \
+
+static inline bool carl9170fw_supports(__le32 list, u8 feature)
+{
+       return le32_to_cpu(list) & BIT(feature);
+}
+
+static inline bool carl9170fw_desc_cmp(const struct carl9170fw_desc_head *head,
+                                      const u8 descid[4], u16 min_len,
+                                      u8 compatible_revision)
+{
+       if (descid[0] == head->magic[0] && descid[1] == head->magic[1] &&
+           descid[2] == head->magic[2] && descid[3] == head->magic[3] &&
+           !CHECK_HDR_VERSION(head, compatible_revision) &&
+           (le16_to_cpu(head->length) >= min_len))
+               return true;
+
+       return false;
+}
+
+#define CARL9170FW_MIN_SIZE    32
+#define CARL9170FW_MAX_SIZE    16384
+
+static inline bool carl9170fw_size_check(unsigned int len)
+{
+       return (len <= CARL9170FW_MAX_SIZE && len >= CARL9170FW_MIN_SIZE);
+}
+
+#endif /* __CARL9170_SHARED_FWDESC_H */
diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h
new file mode 100644 (file)
index 0000000..b1292ac
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * Register map, hardware-specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CARL9170_SHARED_HW_H
+#define __CARL9170_SHARED_HW_H
+
+/* High Speed UART */
+#define        AR9170_UART_REG_BASE                    0x1c0000
+
+/* Definitions of interrupt registers */
+#define        AR9170_UART_REG_RX_BUFFER               (AR9170_UART_REG_BASE + 0x000)
+#define        AR9170_UART_REG_TX_HOLDING              (AR9170_UART_REG_BASE + 0x004)
+#define        AR9170_UART_REG_FIFO_CONTROL            (AR9170_UART_REG_BASE + 0x010)
+#define                AR9170_UART_FIFO_CTRL_RESET_RX_FIFO     0x02
+#define                AR9170_UART_FIFO_CTRL_RESET_TX_FIFO     0x04
+
+#define        AR9170_UART_REG_LINE_CONTROL            (AR9170_UART_REG_BASE + 0x014)
+#define        AR9170_UART_REG_MODEM_CONTROL           (AR9170_UART_REG_BASE + 0x018)
+#define                AR9170_UART_MODEM_CTRL_DTR_BIT          0x01
+#define                AR9170_UART_MODEM_CTRL_RTS_BIT          0x02
+#define                AR9170_UART_MODEM_CTRL_INTERNAL_LOOP_BACK       0x10
+#define                AR9170_UART_MODEM_CTRL_AUTO_RTS         0x20
+#define                AR9170_UART_MODEM_CTRL_AUTO_CTR         0x40
+
+#define        AR9170_UART_REG_LINE_STATUS             (AR9170_UART_REG_BASE + 0x01c)
+#define                AR9170_UART_LINE_STS_RX_DATA_READY      0x01
+#define                AR9170_UART_LINE_STS_RX_BUFFER_OVERRUN  0x02
+#define                AR9170_UART_LINE_STS_RX_BREAK_IND       0x10
+#define                AR9170_UART_LINE_STS_TX_FIFO_NEAR_EMPTY 0x20
+#define                AR9170_UART_LINE_STS_TRANSMITTER_EMPTY  0x40
+
+#define        AR9170_UART_REG_MODEM_STATUS            (AR9170_UART_REG_BASE + 0x020)
+#define                AR9170_UART_MODEM_STS_CTS_CHANGE        0x01
+#define                AR9170_UART_MODEM_STS_DSR_CHANGE        0x02
+#define                AR9170_UART_MODEM_STS_DCD_CHANGE        0x08
+#define                AR9170_UART_MODEM_STS_CTS_COMPL         0x10
+#define                AR9170_UART_MODEM_STS_DSR_COMPL         0x20
+#define                AR9170_UART_MODEM_STS_DCD_COMPL         0x80
+
+#define        AR9170_UART_REG_SCRATCH                 (AR9170_UART_REG_BASE + 0x024)
+#define        AR9170_UART_REG_DIVISOR_LSB             (AR9170_UART_REG_BASE + 0x028)
+#define        AR9170_UART_REG_DIVISOR_MSB             (AR9170_UART_REG_BASE + 0x02c)
+#define        AR9170_UART_REG_WORD_RX_BUFFER          (AR9170_UART_REG_BASE + 0x034)
+#define        AR9170_UART_REG_WORD_TX_HOLDING         (AR9170_UART_REG_BASE + 0x038)
+#define        AR9170_UART_REG_FIFO_COUNT              (AR9170_UART_REG_BASE + 0x03c)
+#define        AR9170_UART_REG_REMAINDER               (AR9170_UART_REG_BASE + 0x04c)
+
+/* Timer */
+#define        AR9170_TIMER_REG_BASE                   0x1c1000
+
+#define        AR9170_TIMER_REG_WATCH_DOG              (AR9170_TIMER_REG_BASE + 0x000)
+#define        AR9170_TIMER_REG_TIMER0                 (AR9170_TIMER_REG_BASE + 0x010)
+#define        AR9170_TIMER_REG_TIMER1                 (AR9170_TIMER_REG_BASE + 0x014)
+#define        AR9170_TIMER_REG_TIMER2                 (AR9170_TIMER_REG_BASE + 0x018)
+#define        AR9170_TIMER_REG_TIMER3                 (AR9170_TIMER_REG_BASE + 0x01c)
+#define        AR9170_TIMER_REG_TIMER4                 (AR9170_TIMER_REG_BASE + 0x020)
+#define        AR9170_TIMER_REG_CONTROL                (AR9170_TIMER_REG_BASE + 0x024)
+#define                AR9170_TIMER_CTRL_DISABLE_CLOCK         0x100
+
+#define        AR9170_TIMER_REG_INTERRUPT              (AR9170_TIMER_REG_BASE + 0x028)
+#define                AR9170_TIMER_INT_TIMER0                 0x001
+#define                AR9170_TIMER_INT_TIMER1                 0x002
+#define                AR9170_TIMER_INT_TIMER2                 0x004
+#define                AR9170_TIMER_INT_TIMER3                 0x008
+#define                AR9170_TIMER_INT_TIMER4                 0x010
+#define                AR9170_TIMER_INT_TICK_TIMER             0x100
+
+#define        AR9170_TIMER_REG_TICK_TIMER             (AR9170_TIMER_REG_BASE + 0x030)
+#define        AR9170_TIMER_REG_CLOCK_LOW              (AR9170_TIMER_REG_BASE + 0x040)
+#define        AR9170_TIMER_REG_CLOCK_HIGH             (AR9170_TIMER_REG_BASE + 0x044)
+
+#define        AR9170_MAC_REG_BASE                     0x1c3000
+
+#define        AR9170_MAC_REG_POWER_STATE_CTRL         (AR9170_MAC_REG_BASE + 0x500)
+#define                AR9170_MAC_POWER_STATE_CTRL_RESET       0x20
+
+#define        AR9170_MAC_REG_MAC_POWER_STATE_CTRL     (AR9170_MAC_REG_BASE + 0x50c)
+
+#define        AR9170_MAC_REG_INT_CTRL                 (AR9170_MAC_REG_BASE + 0x510)
+#define                AR9170_MAC_INT_TXC                      BIT(0)
+#define                AR9170_MAC_INT_RXC                      BIT(1)
+#define                AR9170_MAC_INT_RETRY_FAIL               BIT(2)
+#define                AR9170_MAC_INT_WAKEUP                   BIT(3)
+#define                AR9170_MAC_INT_ATIM                     BIT(4)
+#define                AR9170_MAC_INT_DTIM                     BIT(5)
+#define                AR9170_MAC_INT_CFG_BCN                  BIT(6)
+#define                AR9170_MAC_INT_ABORT                    BIT(7)
+#define                AR9170_MAC_INT_QOS                      BIT(8)
+#define                AR9170_MAC_INT_MIMO_PS                  BIT(9)
+#define                AR9170_MAC_INT_KEY_GEN                  BIT(10)
+#define                AR9170_MAC_INT_DECRY_NOUSER             BIT(11)
+#define                AR9170_MAC_INT_RADAR                    BIT(12)
+#define                AR9170_MAC_INT_QUIET_FRAME              BIT(13)
+#define                AR9170_MAC_INT_PRETBTT                  BIT(14)
+
+#define        AR9170_MAC_REG_TSF_L                    (AR9170_MAC_REG_BASE + 0x514)
+#define        AR9170_MAC_REG_TSF_H                    (AR9170_MAC_REG_BASE + 0x518)
+
+#define        AR9170_MAC_REG_ATIM_WINDOW              (AR9170_MAC_REG_BASE + 0x51c)
+#define                AR9170_MAC_ATIM_PERIOD_S                0
+#define                AR9170_MAC_ATIM_PERIOD                  0x0000ffff
+
+#define        AR9170_MAC_REG_BCN_PERIOD               (AR9170_MAC_REG_BASE + 0x520)
+#define                AR9170_MAC_BCN_PERIOD_S                 0
+#define                AR9170_MAC_BCN_PERIOD                   0x0000ffff
+#define                AR9170_MAC_BCN_DTIM_S                   16
+#define                AR9170_MAC_BCN_DTIM                     0x00ff0000
+#define                AR9170_MAC_BCN_AP_MODE                  BIT(24)
+#define                AR9170_MAC_BCN_IBSS_MODE                BIT(25)
+#define                AR9170_MAC_BCN_PWR_MGT                  BIT(26)
+#define                AR9170_MAC_BCN_STA_PS                   BIT(27)
+
+#define        AR9170_MAC_REG_PRETBTT                  (AR9170_MAC_REG_BASE + 0x524)
+#define                AR9170_MAC_PRETBTT_S                    0
+#define                AR9170_MAC_PRETBTT                      0x0000ffff
+#define                AR9170_MAC_PRETBTT2_S                   16
+#define                AR9170_MAC_PRETBTT2                     0xffff0000
+
+#define        AR9170_MAC_REG_MAC_ADDR_L               (AR9170_MAC_REG_BASE + 0x610)
+#define        AR9170_MAC_REG_MAC_ADDR_H               (AR9170_MAC_REG_BASE + 0x614)
+#define        AR9170_MAC_REG_BSSID_L                  (AR9170_MAC_REG_BASE + 0x618)
+#define        AR9170_MAC_REG_BSSID_H                  (AR9170_MAC_REG_BASE + 0x61c)
+
+#define        AR9170_MAC_REG_GROUP_HASH_TBL_L         (AR9170_MAC_REG_BASE + 0x624)
+#define        AR9170_MAC_REG_GROUP_HASH_TBL_H         (AR9170_MAC_REG_BASE + 0x628)
+
+#define        AR9170_MAC_REG_RX_TIMEOUT               (AR9170_MAC_REG_BASE + 0x62c)
+
+#define        AR9170_MAC_REG_BASIC_RATE               (AR9170_MAC_REG_BASE + 0x630)
+#define        AR9170_MAC_REG_MANDATORY_RATE           (AR9170_MAC_REG_BASE + 0x634)
+#define        AR9170_MAC_REG_RTS_CTS_RATE             (AR9170_MAC_REG_BASE + 0x638)
+#define        AR9170_MAC_REG_BACKOFF_PROTECT          (AR9170_MAC_REG_BASE + 0x63c)
+#define        AR9170_MAC_REG_RX_THRESHOLD             (AR9170_MAC_REG_BASE + 0x640)
+#define        AR9170_MAC_REG_AFTER_PNP                (AR9170_MAC_REG_BASE + 0x648)
+#define        AR9170_MAC_REG_RX_PE_DELAY              (AR9170_MAC_REG_BASE + 0x64c)
+
+#define        AR9170_MAC_REG_DYNAMIC_SIFS_ACK         (AR9170_MAC_REG_BASE + 0x658)
+#define        AR9170_MAC_REG_SNIFFER                  (AR9170_MAC_REG_BASE + 0x674)
+#define                AR9170_MAC_SNIFFER_ENABLE_PROMISC       BIT(0)
+#define                AR9170_MAC_SNIFFER_DEFAULTS             0x02000000
+#define        AR9170_MAC_REG_ENCRYPTION               (AR9170_MAC_REG_BASE + 0x678)
+#define                AR9170_MAC_ENCRYPTION_RX_SOFTWARE       BIT(3)
+#define                AR9170_MAC_ENCRYPTION_DEFAULTS          0x70
+
+#define        AR9170_MAC_REG_MISC_680                 (AR9170_MAC_REG_BASE + 0x680)
+#define        AR9170_MAC_REG_MISC_684                 (AR9170_MAC_REG_BASE + 0x684)
+#define        AR9170_MAC_REG_TX_UNDERRUN              (AR9170_MAC_REG_BASE + 0x688)
+
+#define        AR9170_MAC_REG_FRAMETYPE_FILTER         (AR9170_MAC_REG_BASE + 0x68c)
+#define                AR9170_MAC_FTF_ASSOC_REQ                BIT(0)
+#define                AR9170_MAC_FTF_ASSOC_RESP               BIT(1)
+#define                AR9170_MAC_FTF_REASSOC_REQ              BIT(2)
+#define                AR9170_MAC_FTF_REASSOC_RESP             BIT(3)
+#define                AR9170_MAC_FTF_PRB_REQ                  BIT(4)
+#define                AR9170_MAC_FTF_PRB_RESP                 BIT(5)
+#define                AR9170_MAC_FTF_BIT6                     BIT(6)
+#define                AR9170_MAC_FTF_BIT7                     BIT(7)
+#define                AR9170_MAC_FTF_BEACON                   BIT(8)
+#define                AR9170_MAC_FTF_ATIM                     BIT(9)
+#define                AR9170_MAC_FTF_DEASSOC                  BIT(10)
+#define                AR9170_MAC_FTF_AUTH                     BIT(11)
+#define                AR9170_MAC_FTF_DEAUTH                   BIT(12)
+#define                AR9170_MAC_FTF_BIT13                    BIT(13)
+#define                AR9170_MAC_FTF_BIT14                    BIT(14)
+#define                AR9170_MAC_FTF_BIT15                    BIT(15)
+#define                AR9170_MAC_FTF_BAR                      BIT(24)
+#define                AR9170_MAC_FTF_BA                       BIT(25)
+#define                AR9170_MAC_FTF_PSPOLL                   BIT(26)
+#define                AR9170_MAC_FTF_RTS                      BIT(27)
+#define                AR9170_MAC_FTF_CTS                      BIT(28)
+#define                AR9170_MAC_FTF_ACK                      BIT(29)
+#define                AR9170_MAC_FTF_CFE                      BIT(30)
+#define                AR9170_MAC_FTF_CFE_ACK                  BIT(31)
+#define                AR9170_MAC_FTF_DEFAULTS                 0x0500ffff
+#define                AR9170_MAC_FTF_MONITOR                  0xff00ffff
+
+#define        AR9170_MAC_REG_ACK_EXTENSION            (AR9170_MAC_REG_BASE + 0x690)
+#define        AR9170_MAC_REG_ACK_TPC                  (AR9170_MAC_REG_BASE + 0x694)
+#define        AR9170_MAC_REG_EIFS_AND_SIFS            (AR9170_MAC_REG_BASE + 0x698)
+#define        AR9170_MAC_REG_RX_TIMEOUT_COUNT         (AR9170_MAC_REG_BASE + 0x69c)
+#define        AR9170_MAC_REG_RX_TOTAL                 (AR9170_MAC_REG_BASE + 0x6a0)
+#define        AR9170_MAC_REG_RX_CRC32                 (AR9170_MAC_REG_BASE + 0x6a4)
+#define        AR9170_MAC_REG_RX_CRC16                 (AR9170_MAC_REG_BASE + 0x6a8)
+#define        AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI    (AR9170_MAC_REG_BASE + 0x6ac)
+#define        AR9170_MAC_REG_RX_OVERRUN               (AR9170_MAC_REG_BASE + 0x6b0)
+#define        AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL    (AR9170_MAC_REG_BASE + 0x6bc)
+#define AR9170_MAC_REG_TX_BLOCKACKS            (AR9170_MAC_REG_BASE + 0x6c0)
+#define AR9170_MAC_REG_NAV_COUNT               (AR9170_MAC_REG_BASE + 0x6c4)
+#define AR9170_MAC_REG_BACKOFF_STATUS          (AR9170_MAC_REG_BASE + 0x6c8)
+#define        AR9170_MAC_REG_TX_RETRY                 (AR9170_MAC_REG_BASE + 0x6cc)
+
+#define AR9170_MAC_REG_TX_COMPLETE             (AR9170_MAC_REG_BASE + 0x6d4)
+
+#define        AR9170_MAC_REG_CHANNEL_BUSY             (AR9170_MAC_REG_BASE + 0x6e8)
+#define        AR9170_MAC_REG_EXT_BUSY                 (AR9170_MAC_REG_BASE + 0x6ec)
+
+#define        AR9170_MAC_REG_SLOT_TIME                (AR9170_MAC_REG_BASE + 0x6f0)
+#define        AR9170_MAC_REG_TX_TOTAL                 (AR9170_MAC_REG_BASE + 0x6f4)
+#define AR9170_MAC_REG_ACK_FC                  (AR9170_MAC_REG_BASE + 0x6f8)
+
+#define        AR9170_MAC_REG_CAM_MODE                 (AR9170_MAC_REG_BASE + 0x700)
+#define                AR9170_MAC_CAM_IBSS                     0xe0
+#define                AR9170_MAC_CAM_AP                       0xa1
+#define                AR9170_MAC_CAM_STA                      0x2
+#define                AR9170_MAC_CAM_AP_WDS                   0x3
+#define                AR9170_MAC_CAM_DEFAULTS                 (0xf << 24)
+#define                AR9170_MAC_CAM_HOST_PENDING             0x80000000
+
+#define        AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L      (AR9170_MAC_REG_BASE + 0x704)
+#define        AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H      (AR9170_MAC_REG_BASE + 0x708)
+
+#define        AR9170_MAC_REG_CAM_ADDR                 (AR9170_MAC_REG_BASE + 0x70c)
+#define                AR9170_MAC_CAM_ADDR_WRITE               0x80000000
+#define        AR9170_MAC_REG_CAM_DATA0                (AR9170_MAC_REG_BASE + 0x720)
+#define        AR9170_MAC_REG_CAM_DATA1                (AR9170_MAC_REG_BASE + 0x724)
+#define        AR9170_MAC_REG_CAM_DATA2                (AR9170_MAC_REG_BASE + 0x728)
+#define        AR9170_MAC_REG_CAM_DATA3                (AR9170_MAC_REG_BASE + 0x72c)
+
+#define        AR9170_MAC_REG_CAM_DBG0                 (AR9170_MAC_REG_BASE + 0x730)
+#define        AR9170_MAC_REG_CAM_DBG1                 (AR9170_MAC_REG_BASE + 0x734)
+#define        AR9170_MAC_REG_CAM_DBG2                 (AR9170_MAC_REG_BASE + 0x738)
+#define        AR9170_MAC_REG_CAM_STATE                (AR9170_MAC_REG_BASE + 0x73c)
+#define                AR9170_MAC_CAM_STATE_READ_PENDING       0x40000000
+#define                AR9170_MAC_CAM_STATE_WRITE_PENDING      0x80000000
+
+#define        AR9170_MAC_REG_CAM_TXKEY                (AR9170_MAC_REG_BASE + 0x740)
+#define        AR9170_MAC_REG_CAM_RXKEY                (AR9170_MAC_REG_BASE + 0x750)
+
+#define        AR9170_MAC_REG_CAM_TX_ENC_TYPE          (AR9170_MAC_REG_BASE + 0x760)
+#define        AR9170_MAC_REG_CAM_RX_ENC_TYPE          (AR9170_MAC_REG_BASE + 0x770)
+#define        AR9170_MAC_REG_CAM_TX_SERACH_HIT        (AR9170_MAC_REG_BASE + 0x780)
+#define        AR9170_MAC_REG_CAM_RX_SERACH_HIT        (AR9170_MAC_REG_BASE + 0x790)
+
+#define        AR9170_MAC_REG_AC0_CW                   (AR9170_MAC_REG_BASE + 0xb00)
+#define        AR9170_MAC_REG_AC1_CW                   (AR9170_MAC_REG_BASE + 0xb04)
+#define        AR9170_MAC_REG_AC2_CW                   (AR9170_MAC_REG_BASE + 0xb08)
+#define        AR9170_MAC_REG_AC3_CW                   (AR9170_MAC_REG_BASE + 0xb0c)
+#define        AR9170_MAC_REG_AC4_CW                   (AR9170_MAC_REG_BASE + 0xb10)
+#define        AR9170_MAC_REG_AC2_AC1_AC0_AIFS         (AR9170_MAC_REG_BASE + 0xb14)
+#define        AR9170_MAC_REG_AC4_AC3_AC2_AIFS         (AR9170_MAC_REG_BASE + 0xb18)
+#define AR9170_MAC_REG_TXOP_ACK_EXTENSION      (AR9170_MAC_REG_BASE + 0xb1c)
+#define AR9170_MAC_REG_TXOP_ACK_INTERVAL       (AR9170_MAC_REG_BASE + 0xb20)
+#define AR9170_MAC_REG_CONTENTION_POINT                (AR9170_MAC_REG_BASE + 0xb24)
+#define        AR9170_MAC_REG_RETRY_MAX                (AR9170_MAC_REG_BASE + 0xb28)
+#define AR9170_MAC_REG_TID_CFACK_CFEND_RATE    (AR9170_MAC_REG_BASE + 0xb2c)
+#define        AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND      (AR9170_MAC_REG_BASE + 0xb30)
+#define AR9170_MAC_REG_TKIP_TSC                        (AR9170_MAC_REG_BASE + 0xb34)
+#define AR9170_MAC_REG_TXOP_DURATION           (AR9170_MAC_REG_BASE + 0xb38)
+#define AR9170_MAC_REG_TX_QOS_THRESHOLD                (AR9170_MAC_REG_BASE + 0xb3c)
+#define        AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA (AR9170_MAC_REG_BASE + 0xb40)
+#define                AR9170_MAC_VIRTUAL_CCA_Q0               BIT(15)
+#define                AR9170_MAC_VIRTUAL_CCA_Q1               BIT(16)
+#define                AR9170_MAC_VIRTUAL_CCA_Q2               BIT(17)
+#define                AR9170_MAC_VIRTUAL_CCA_Q3               BIT(18)
+#define                AR9170_MAC_VIRTUAL_CCA_Q4               BIT(19)
+#define                AR9170_MAC_VIRTUAL_CCA_ALL              (0xf8000)
+
+#define        AR9170_MAC_REG_AC1_AC0_TXOP             (AR9170_MAC_REG_BASE + 0xb44)
+#define        AR9170_MAC_REG_AC3_AC2_TXOP             (AR9170_MAC_REG_BASE + 0xb48)
+
+#define        AR9170_MAC_REG_AMPDU_COUNT              (AR9170_MAC_REG_BASE + 0xb88)
+#define        AR9170_MAC_REG_MPDU_COUNT               (AR9170_MAC_REG_BASE + 0xb8c)
+
+#define        AR9170_MAC_REG_AMPDU_FACTOR             (AR9170_MAC_REG_BASE + 0xb9c)
+#define                AR9170_MAC_AMPDU_FACTOR                 0x7f0000
+#define                AR9170_MAC_AMPDU_FACTOR_S               16
+#define        AR9170_MAC_REG_AMPDU_DENSITY            (AR9170_MAC_REG_BASE + 0xba0)
+#define                AR9170_MAC_AMPDU_DENSITY                0x7
+#define                AR9170_MAC_AMPDU_DENSITY_S              0
+
+#define        AR9170_MAC_REG_FCS_SELECT               (AR9170_MAC_REG_BASE + 0xbb0)
+#define                AR9170_MAC_FCS_SWFCS                    0x1
+#define                AR9170_MAC_FCS_FIFO_PROT                0x4
+
+#define        AR9170_MAC_REG_RTS_CTS_TPC              (AR9170_MAC_REG_BASE + 0xbb4)
+#define AR9170_MAC_REG_CFEND_QOSNULL_TPC       (AR9170_MAC_REG_BASE + 0xbb8)
+
+#define        AR9170_MAC_REG_ACK_TABLE                (AR9170_MAC_REG_BASE + 0xc00)
+#define AR9170_MAC_REG_RX_CONTROL              (AR9170_MAC_REG_BASE + 0xc40)
+#define                AR9170_MAC_RX_CTRL_DEAGG                0x1
+#define                AR9170_MAC_RX_CTRL_SHORT_FILTER         0x2
+#define                AR9170_MAC_RX_CTRL_SA_DA_SEARCH         0x20
+#define                AR9170_MAC_RX_CTRL_PASS_TO_HOST         BIT(28)
+#define                AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER       BIT(30)
+
+#define AR9170_MAC_REG_RX_CONTROL_1            (AR9170_MAC_REG_BASE + 0xc44)
+
+#define        AR9170_MAC_REG_AMPDU_RX_THRESH          (AR9170_MAC_REG_BASE + 0xc50)
+
+#define        AR9170_MAC_REG_RX_MPDU                  (AR9170_MAC_REG_BASE + 0xca0)
+#define        AR9170_MAC_REG_RX_DROPPED_MPDU          (AR9170_MAC_REG_BASE + 0xca4)
+#define        AR9170_MAC_REG_RX_DEL_MPDU              (AR9170_MAC_REG_BASE + 0xca8)
+#define        AR9170_MAC_REG_RX_PHY_MISC_ERROR        (AR9170_MAC_REG_BASE + 0xcac)
+#define        AR9170_MAC_REG_RX_PHY_XR_ERROR          (AR9170_MAC_REG_BASE + 0xcb0)
+#define        AR9170_MAC_REG_RX_PHY_OFDM_ERROR        (AR9170_MAC_REG_BASE + 0xcb4)
+#define        AR9170_MAC_REG_RX_PHY_CCK_ERROR         (AR9170_MAC_REG_BASE + 0xcb8)
+#define        AR9170_MAC_REG_RX_PHY_HT_ERROR          (AR9170_MAC_REG_BASE + 0xcbc)
+#define        AR9170_MAC_REG_RX_PHY_TOTAL             (AR9170_MAC_REG_BASE + 0xcc0)
+
+#define        AR9170_MAC_REG_DMA_TXQ_ADDR             (AR9170_MAC_REG_BASE + 0xd00)
+#define        AR9170_MAC_REG_DMA_TXQ_CURR_ADDR        (AR9170_MAC_REG_BASE + 0xd04)
+#define        AR9170_MAC_REG_DMA_TXQ0_ADDR            (AR9170_MAC_REG_BASE + 0xd00)
+#define        AR9170_MAC_REG_DMA_TXQ0_CURR_ADDR       (AR9170_MAC_REG_BASE + 0xd04)
+#define        AR9170_MAC_REG_DMA_TXQ1_ADDR            (AR9170_MAC_REG_BASE + 0xd08)
+#define        AR9170_MAC_REG_DMA_TXQ1_CURR_ADDR       (AR9170_MAC_REG_BASE + 0xd0c)
+#define        AR9170_MAC_REG_DMA_TXQ2_ADDR            (AR9170_MAC_REG_BASE + 0xd10)
+#define        AR9170_MAC_REG_DMA_TXQ2_CURR_ADDR       (AR9170_MAC_REG_BASE + 0xd14)
+#define        AR9170_MAC_REG_DMA_TXQ3_ADDR            (AR9170_MAC_REG_BASE + 0xd18)
+#define        AR9170_MAC_REG_DMA_TXQ3_CURR_ADDR       (AR9170_MAC_REG_BASE + 0xd1c)
+#define        AR9170_MAC_REG_DMA_TXQ4_ADDR            (AR9170_MAC_REG_BASE + 0xd20)
+#define        AR9170_MAC_REG_DMA_TXQ4_CURR_ADDR       (AR9170_MAC_REG_BASE + 0xd24)
+#define        AR9170_MAC_REG_DMA_RXQ_ADDR             (AR9170_MAC_REG_BASE + 0xd28)
+#define        AR9170_MAC_REG_DMA_RXQ_CURR_ADDR        (AR9170_MAC_REG_BASE + 0xd2c)
+
+#define        AR9170_MAC_REG_DMA_TRIGGER              (AR9170_MAC_REG_BASE + 0xd30)
+#define                AR9170_DMA_TRIGGER_TXQ0                 BIT(0)
+#define                AR9170_DMA_TRIGGER_TXQ1                 BIT(1)
+#define                AR9170_DMA_TRIGGER_TXQ2                 BIT(2)
+#define                AR9170_DMA_TRIGGER_TXQ3                 BIT(3)
+#define                AR9170_DMA_TRIGGER_TXQ4                 BIT(4)
+#define                AR9170_DMA_TRIGGER_RXQ                  BIT(8)
+
+#define AR9170_MAC_REG_DMA_WLAN_STATUS         (AR9170_MAC_REG_BASE + 0xd38)
+#define        AR9170_MAC_REG_DMA_STATUS               (AR9170_MAC_REG_BASE + 0xd3c)
+
+#define        AR9170_MAC_REG_TXRX_MPI                 (AR9170_MAC_REG_BASE + 0xd7c)
+#define                AR9170_MAC_TXRX_MPI_TX_MPI_MASK         0x0000000f
+#define                AR9170_MAC_TXRX_MPI_TX_TO_MASK          0x0000fff0
+#define                AR9170_MAC_TXRX_MPI_RX_MPI_MASK         0x000f0000
+#define                AR9170_MAC_TXRX_MPI_RX_TO_MASK          0xfff00000
+
+#define        AR9170_MAC_REG_BCN_ADDR                 (AR9170_MAC_REG_BASE + 0xd84)
+#define        AR9170_MAC_REG_BCN_LENGTH               (AR9170_MAC_REG_BASE + 0xd88)
+#define                AR9170_MAC_BCN_LENGTH_MAX               256
+
+#define AR9170_MAC_REG_BCN_STATUS              (AR9170_MAC_REG_BASE + 0xd8c)
+
+#define        AR9170_MAC_REG_BCN_PLCP                 (AR9170_MAC_REG_BASE + 0xd90)
+#define        AR9170_MAC_REG_BCN_CTRL                 (AR9170_MAC_REG_BASE + 0xd94)
+#define                AR9170_BCN_CTRL_READY                   0x01
+#define                AR9170_BCN_CTRL_LOCK                    0x02
+
+#define AR9170_MAC_REG_BCN_CURR_ADDR           (AR9170_MAC_REG_BASE + 0xd98)
+#define        AR9170_MAC_REG_BCN_COUNT                (AR9170_MAC_REG_BASE + 0xd9c)
+
+
+#define        AR9170_MAC_REG_BCN_HT1                  (AR9170_MAC_REG_BASE + 0xda0)
+#define        AR9170_MAC_REG_BCN_HT2                  (AR9170_MAC_REG_BASE + 0xda4)
+
+#define        AR9170_MAC_REG_DMA_TXQX_ADDR_CURR       (AR9170_MAC_REG_BASE + 0xdc0)
+
+/* Random number generator */
+#define        AR9170_RAND_REG_BASE                    0x1d0000
+
+#define        AR9170_RAND_REG_NUM                     (AR9170_RAND_REG_BASE + 0x000)
+#define        AR9170_RAND_REG_MODE                    (AR9170_RAND_REG_BASE + 0x004)
+#define                AR9170_RAND_MODE_MANUAL                 0x000
+#define                AR9170_RAND_MODE_FREE                   0x001
+
+/* GPIO */
+#define        AR9170_GPIO_REG_BASE                    0x1d0100
+#define        AR9170_GPIO_REG_PORT_TYPE               (AR9170_GPIO_REG_BASE + 0x000)
+#define        AR9170_GPIO_REG_PORT_DATA               (AR9170_GPIO_REG_BASE + 0x004)
+#define                AR9170_GPIO_PORT_LED_0                  1
+#define                AR9170_GPIO_PORT_LED_1                  2
+/* WPS Button GPIO for TP-Link TL-WN821N */
+#define                AR9170_GPIO_PORT_WPS_BUTTON_PRESSED     4
+
+/* Memory Controller */
+#define        AR9170_MC_REG_BASE                      0x1d1000
+
+#define        AR9170_MC_REG_FLASH_WAIT_STATE          (AR9170_MC_REG_BASE + 0x000)
+#define        AR9170_MC_REG_SEEPROM_WP0               (AR9170_MC_REG_BASE + 0x400)
+#define        AR9170_MC_REG_SEEPROM_WP1               (AR9170_MC_REG_BASE + 0x404)
+#define        AR9170_MC_REG_SEEPROM_WP2               (AR9170_MC_REG_BASE + 0x408)
+
+/* Interrupt Controller */
+#define        AR9170_MAX_INT_SRC                      9
+#define        AR9170_INT_REG_BASE                     0x1d2000
+
+#define        AR9170_INT_REG_FLAG                     (AR9170_INT_REG_BASE + 0x000)
+#define        AR9170_INT_REG_FIQ_MASK                 (AR9170_INT_REG_BASE + 0x004)
+#define        AR9170_INT_REG_IRQ_MASK                 (AR9170_INT_REG_BASE + 0x008)
+/* INT_REG_FLAG, INT_REG_FIQ_MASK and INT_REG_IRQ_MASK */
+#define                AR9170_INT_FLAG_WLAN                    0x001
+#define                AR9170_INT_FLAG_PTAB_BIT                0x002
+#define                AR9170_INT_FLAG_SE_BIT                  0x004
+#define                AR9170_INT_FLAG_UART_BIT                0x008
+#define                AR9170_INT_FLAG_TIMER_BIT               0x010
+#define                AR9170_INT_FLAG_EXT_BIT                 0x020
+#define                AR9170_INT_FLAG_SW_BIT                  0x040
+#define                AR9170_INT_FLAG_USB_BIT                 0x080
+#define                AR9170_INT_FLAG_ETHERNET_BIT            0x100
+
+#define        AR9170_INT_REG_PRIORITY1                (AR9170_INT_REG_BASE + 0x00c)
+#define        AR9170_INT_REG_PRIORITY2                (AR9170_INT_REG_BASE + 0x010)
+#define        AR9170_INT_REG_PRIORITY3                (AR9170_INT_REG_BASE + 0x014)
+#define        AR9170_INT_REG_EXT_INT_CONTROL          (AR9170_INT_REG_BASE + 0x018)
+#define        AR9170_INT_REG_SW_INT_CONTROL           (AR9170_INT_REG_BASE + 0x01c)
+#define                AR9170_INT_SW_INT_ENABLE                0x1
+
+#define        AR9170_INT_REG_FIQ_ENCODE               (AR9170_INT_REG_BASE + 0x020)
+#define        AR9170_INT_INT_IRQ_ENCODE               (AR9170_INT_REG_BASE + 0x024)
+
+/* Power Management */
+#define        AR9170_PWR_REG_BASE                     0x1d4000
+
+#define AR9170_PWR_REG_POWER_STATE             (AR9170_PWR_REG_BASE + 0x000)
+
+#define        AR9170_PWR_REG_RESET                    (AR9170_PWR_REG_BASE + 0x004)
+#define                AR9170_PWR_RESET_COMMIT_RESET_MASK      BIT(0)
+#define                AR9170_PWR_RESET_WLAN_MASK              BIT(1)
+#define                AR9170_PWR_RESET_DMA_MASK               BIT(2)
+#define                AR9170_PWR_RESET_BRIDGE_MASK            BIT(3)
+#define                AR9170_PWR_RESET_AHB_MASK               BIT(9)
+#define                AR9170_PWR_RESET_BB_WARM_RESET          BIT(10)
+#define                AR9170_PWR_RESET_BB_COLD_RESET          BIT(11)
+#define                AR9170_PWR_RESET_ADDA_CLK_COLD_RESET    BIT(12)
+#define                AR9170_PWR_RESET_PLL                    BIT(13)
+#define                AR9170_PWR_RESET_USB_PLL                BIT(14)
+
+#define        AR9170_PWR_REG_CLOCK_SEL                (AR9170_PWR_REG_BASE + 0x008)
+#define                AR9170_PWR_CLK_AHB_40MHZ                0
+#define                AR9170_PWR_CLK_AHB_20_22MHZ             1
+#define                AR9170_PWR_CLK_AHB_40_44MHZ             2
+#define                AR9170_PWR_CLK_AHB_80_88MHZ             3
+#define                AR9170_PWR_CLK_DAC_160_INV_DLY          0x70
+
+#define        AR9170_PWR_REG_CHIP_REVISION            (AR9170_PWR_REG_BASE + 0x010)
+#define AR9170_PWR_REG_PLL_ADDAC               (AR9170_PWR_REG_BASE + 0x014)
+#define        AR9170_PWR_REG_WATCH_DOG_MAGIC          (AR9170_PWR_REG_BASE + 0x020)
+
+/* Faraday USB Controller */
+#define        AR9170_USB_REG_BASE                     0x1e1000
+
+#define        AR9170_USB_REG_MAIN_CTRL                (AR9170_USB_REG_BASE + 0x000)
+#define                AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP      BIT(0)
+#define                AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT  BIT(2)
+#define                AR9170_USB_MAIN_CTRL_HIGHSPEED          BIT(6)
+
+#define        AR9170_USB_REG_DEVICE_ADDRESS           (AR9170_USB_REG_BASE + 0x001)
+#define                AR9170_USB_DEVICE_ADDRESS_CONFIGURE     BIT(7)
+
+#define        AR9170_USB_REG_TEST                     (AR9170_USB_REG_BASE + 0x002)
+#define        AR9170_USB_REG_PHY_TEST_SELECT          (AR9170_USB_REG_BASE + 0x008)
+#define        AR9170_USB_REG_CX_CONFIG_STATUS         (AR9170_USB_REG_BASE + 0x00b)
+#define        AR9170_USB_REG_EP0_DATA                 (AR9170_USB_REG_BASE + 0x00c)
+#define        AR9170_USB_REG_EP0_DATA1                (AR9170_USB_REG_BASE + 0x00c)
+#define        AR9170_USB_REG_EP0_DATA2                (AR9170_USB_REG_BASE + 0x00d)
+
+#define        AR9170_USB_REG_INTR_MASK_BYTE_0         (AR9170_USB_REG_BASE + 0x011)
+#define        AR9170_USB_REG_INTR_MASK_BYTE_1         (AR9170_USB_REG_BASE + 0x012)
+#define        AR9170_USB_REG_INTR_MASK_BYTE_2         (AR9170_USB_REG_BASE + 0x013)
+#define        AR9170_USB_REG_INTR_MASK_BYTE_3         (AR9170_USB_REG_BASE + 0x014)
+#define        AR9170_USB_REG_INTR_MASK_BYTE_4         (AR9170_USB_REG_BASE + 0x015)
+#define                AR9170_USB_INTR_DISABLE_OUT_INT         (BIT(7) | BIT(6))
+
+#define        AR9170_USB_REG_INTR_MASK_BYTE_5         (AR9170_USB_REG_BASE + 0x016)
+#define        AR9170_USB_REG_INTR_MASK_BYTE_6         (AR9170_USB_REG_BASE + 0x017)
+#define                AR9170_USB_INTR_DISABLE_IN_INT          BIT(6)
+
+#define        AR9170_USB_REG_INTR_MASK_BYTE_7         (AR9170_USB_REG_BASE + 0x018)
+
+#define        AR9170_USB_REG_INTR_GROUP               (AR9170_USB_REG_BASE + 0x020)
+
+#define        AR9170_USB_REG_INTR_SOURCE_0            (AR9170_USB_REG_BASE + 0x021)
+#define        AR9170_USB_REG_INTR_SOURCE_1            (AR9170_USB_REG_BASE + 0x022)
+#define        AR9170_USB_REG_INTR_SOURCE_2            (AR9170_USB_REG_BASE + 0x023)
+#define        AR9170_USB_REG_INTR_SOURCE_3            (AR9170_USB_REG_BASE + 0x024)
+#define        AR9170_USB_REG_INTR_SOURCE_4            (AR9170_USB_REG_BASE + 0x025)
+#define        AR9170_USB_REG_INTR_SOURCE_5            (AR9170_USB_REG_BASE + 0x026)
+#define        AR9170_USB_REG_INTR_SOURCE_6            (AR9170_USB_REG_BASE + 0x027)
+#define        AR9170_USB_REG_INTR_SOURCE_7            (AR9170_USB_REG_BASE + 0x028)
+
+#define        AR9170_USB_REG_EP_MAP                   (AR9170_USB_REG_BASE + 0x030)
+#define        AR9170_USB_REG_EP1_MAP                  (AR9170_USB_REG_BASE + 0x030)
+#define        AR9170_USB_REG_EP2_MAP                  (AR9170_USB_REG_BASE + 0x031)
+#define        AR9170_USB_REG_EP3_MAP                  (AR9170_USB_REG_BASE + 0x032)
+#define        AR9170_USB_REG_EP4_MAP                  (AR9170_USB_REG_BASE + 0x033)
+#define        AR9170_USB_REG_EP5_MAP                  (AR9170_USB_REG_BASE + 0x034)
+#define        AR9170_USB_REG_EP6_MAP                  (AR9170_USB_REG_BASE + 0x035)
+#define        AR9170_USB_REG_EP7_MAP                  (AR9170_USB_REG_BASE + 0x036)
+#define        AR9170_USB_REG_EP8_MAP                  (AR9170_USB_REG_BASE + 0x037)
+#define        AR9170_USB_REG_EP9_MAP                  (AR9170_USB_REG_BASE + 0x038)
+#define        AR9170_USB_REG_EP10_MAP                 (AR9170_USB_REG_BASE + 0x039)
+
+#define        AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH      (AR9170_USB_REG_BASE + 0x03f)
+#define                AR9170_USB_EP_IN_TOGGLE                 0x10
+
+#define        AR9170_USB_REG_EP_IN_MAX_SIZE_LOW       (AR9170_USB_REG_BASE + 0x03e)
+
+#define        AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH     (AR9170_USB_REG_BASE + 0x05f)
+#define                AR9170_USB_EP_OUT_TOGGLE                0x10
+
+#define        AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW      (AR9170_USB_REG_BASE + 0x05e)
+
+#define        AR9170_USB_REG_EP3_BYTE_COUNT_HIGH      (AR9170_USB_REG_BASE + 0x0ae)
+#define        AR9170_USB_REG_EP3_BYTE_COUNT_LOW       (AR9170_USB_REG_BASE + 0x0be)
+#define        AR9170_USB_REG_EP4_BYTE_COUNT_HIGH      (AR9170_USB_REG_BASE + 0x0af)
+#define        AR9170_USB_REG_EP4_BYTE_COUNT_LOW       (AR9170_USB_REG_BASE + 0x0bf)
+
+#define        AR9170_USB_REG_FIFO_MAP                 (AR9170_USB_REG_BASE + 0x080)
+#define        AR9170_USB_REG_FIFO0_MAP                (AR9170_USB_REG_BASE + 0x080)
+#define        AR9170_USB_REG_FIFO1_MAP                (AR9170_USB_REG_BASE + 0x081)
+#define        AR9170_USB_REG_FIFO2_MAP                (AR9170_USB_REG_BASE + 0x082)
+#define        AR9170_USB_REG_FIFO3_MAP                (AR9170_USB_REG_BASE + 0x083)
+#define        AR9170_USB_REG_FIFO4_MAP                (AR9170_USB_REG_BASE + 0x084)
+#define        AR9170_USB_REG_FIFO5_MAP                (AR9170_USB_REG_BASE + 0x085)
+#define        AR9170_USB_REG_FIFO6_MAP                (AR9170_USB_REG_BASE + 0x086)
+#define        AR9170_USB_REG_FIFO7_MAP                (AR9170_USB_REG_BASE + 0x087)
+#define        AR9170_USB_REG_FIFO8_MAP                (AR9170_USB_REG_BASE + 0x088)
+#define        AR9170_USB_REG_FIFO9_MAP                (AR9170_USB_REG_BASE + 0x089)
+
+#define        AR9170_USB_REG_FIFO_CONFIG              (AR9170_USB_REG_BASE + 0x090)
+#define        AR9170_USB_REG_FIFO0_CONFIG             (AR9170_USB_REG_BASE + 0x090)
+#define        AR9170_USB_REG_FIFO1_CONFIG             (AR9170_USB_REG_BASE + 0x091)
+#define        AR9170_USB_REG_FIFO2_CONFIG             (AR9170_USB_REG_BASE + 0x092)
+#define        AR9170_USB_REG_FIFO3_CONFIG             (AR9170_USB_REG_BASE + 0x093)
+#define        AR9170_USB_REG_FIFO4_CONFIG             (AR9170_USB_REG_BASE + 0x094)
+#define        AR9170_USB_REG_FIFO5_CONFIG             (AR9170_USB_REG_BASE + 0x095)
+#define        AR9170_USB_REG_FIFO6_CONFIG             (AR9170_USB_REG_BASE + 0x096)
+#define        AR9170_USB_REG_FIFO7_CONFIG             (AR9170_USB_REG_BASE + 0x097)
+#define        AR9170_USB_REG_FIFO8_CONFIG             (AR9170_USB_REG_BASE + 0x098)
+#define        AR9170_USB_REG_FIFO9_CONFIG             (AR9170_USB_REG_BASE + 0x099)
+
+#define        AR9170_USB_REG_EP3_DATA                 (AR9170_USB_REG_BASE + 0x0f8)
+#define        AR9170_USB_REG_EP4_DATA                 (AR9170_USB_REG_BASE + 0x0fc)
+
+#define        AR9170_USB_REG_FIFO_SIZE                (AR9170_USB_REG_BASE + 0x100)
+#define        AR9170_USB_REG_DMA_CTL                  (AR9170_USB_REG_BASE + 0x108)
+#define                AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE     BIT(0)
+#define                AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE   BIT(1)
+#define                AR9170_USB_DMA_CTL_HIGH_SPEED           BIT(2)
+#define                AR9170_USB_DMA_CTL_UP_PACKET_MODE       BIT(3)
+#define                AR9170_USB_DMA_CTL_UP_STREAM_S          4
+#define                AR9170_USB_DMA_CTL_UP_STREAM            (BIT(4) | BIT(5))
+#define                AR9170_USB_DMA_CTL_UP_STREAM_4K         (0)
+#define                AR9170_USB_DMA_CTL_UP_STREAM_8K         BIT(4)
+#define                AR9170_USB_DMA_CTL_UP_STREAM_16K        BIT(5)
+#define                AR9170_USB_DMA_CTL_UP_STREAM_32K        (BIT(4) | BIT(5))
+#define                AR9170_USB_DMA_CTL_DOWN_STREAM          BIT(6)
+
+#define        AR9170_USB_REG_DMA_STATUS               (AR9170_USB_REG_BASE + 0x10c)
+#define                AR9170_USB_DMA_STATUS_UP_IDLE           BIT(8)
+#define                AR9170_USB_DMA_STATUS_DN_IDLE           BIT(16)
+
+#define        AR9170_USB_REG_MAX_AGG_UPLOAD           (AR9170_USB_REG_BASE + 0x110)
+#define        AR9170_USB_REG_UPLOAD_TIME_CTL          (AR9170_USB_REG_BASE + 0x114)
+#define        AR9170_USB_REG_CBUS_CTRL                (AR9170_USB_REG_BASE + 0x1f0)
+#define                AR9170_USB_CBUS_CTRL_BUFFER_END         (BIT(1))
+
+/* PCI/USB to AHB Bridge */
+#define        AR9170_PTA_REG_BASE                     0x1e2000
+
+#define        AR9170_PTA_REG_CMD                      (AR9170_PTA_REG_BASE + 0x000)
+#define        AR9170_PTA_REG_PARAM1                   (AR9170_PTA_REG_BASE + 0x004)
+#define        AR9170_PTA_REG_PARAM2                   (AR9170_PTA_REG_BASE + 0x008)
+#define        AR9170_PTA_REG_PARAM3                   (AR9170_PTA_REG_BASE + 0x00c)
+#define        AR9170_PTA_REG_RSP                      (AR9170_PTA_REG_BASE + 0x010)
+#define        AR9170_PTA_REG_STATUS1                  (AR9170_PTA_REG_BASE + 0x014)
+#define        AR9170_PTA_REG_STATUS2                  (AR9170_PTA_REG_BASE + 0x018)
+#define        AR9170_PTA_REG_STATUS3                  (AR9170_PTA_REG_BASE + 0x01c)
+#define        AR9170_PTA_REG_AHB_INT_FLAG             (AR9170_PTA_REG_BASE + 0x020)
+#define        AR9170_PTA_REG_AHB_INT_MASK             (AR9170_PTA_REG_BASE + 0x024)
+#define        AR9170_PTA_REG_AHB_INT_ACK              (AR9170_PTA_REG_BASE + 0x028)
+#define        AR9170_PTA_REG_AHB_SCRATCH1             (AR9170_PTA_REG_BASE + 0x030)
+#define        AR9170_PTA_REG_AHB_SCRATCH2             (AR9170_PTA_REG_BASE + 0x034)
+#define        AR9170_PTA_REG_AHB_SCRATCH3             (AR9170_PTA_REG_BASE + 0x038)
+#define        AR9170_PTA_REG_AHB_SCRATCH4             (AR9170_PTA_REG_BASE + 0x03c)
+
+#define        AR9170_PTA_REG_SHARE_MEM_CTRL           (AR9170_PTA_REG_BASE + 0x124)
+
+/*
+ * PCI to AHB Bridge
+ */
+
+#define        AR9170_PTA_REG_INT_FLAG                 (AR9170_PTA_REG_BASE + 0x100)
+#define                AR9170_PTA_INT_FLAG_DN                  0x01
+#define                AR9170_PTA_INT_FLAG_UP                  0x02
+#define                AR9170_PTA_INT_FLAG_CMD                 0x04
+
+#define        AR9170_PTA_REG_INT_MASK                 (AR9170_PTA_REG_BASE + 0x104)
+#define        AR9170_PTA_REG_DN_DMA_ADDRL             (AR9170_PTA_REG_BASE + 0x108)
+#define        AR9170_PTA_REG_DN_DMA_ADDRH             (AR9170_PTA_REG_BASE + 0x10c)
+#define        AR9170_PTA_REG_UP_DMA_ADDRL             (AR9170_PTA_REG_BASE + 0x110)
+#define        AR9170_PTA_REG_UP_DMA_ADDRH             (AR9170_PTA_REG_BASE + 0x114)
+#define        AR9170_PTA_REG_DN_PEND_TIME             (AR9170_PTA_REG_BASE + 0x118)
+#define        AR9170_PTA_REG_UP_PEND_TIME             (AR9170_PTA_REG_BASE + 0x11c)
+#define        AR9170_PTA_REG_CONTROL                  (AR9170_PTA_REG_BASE + 0x120)
+#define                AR9170_PTA_CTRL_4_BEAT_BURST            0x00
+#define                AR9170_PTA_CTRL_8_BEAT_BURST            0x01
+#define                AR9170_PTA_CTRL_16_BEAT_BURST           0x02
+#define                AR9170_PTA_CTRL_LOOPBACK_MODE           0x10
+
+#define        AR9170_PTA_REG_MEM_CTRL                 (AR9170_PTA_REG_BASE + 0x124)
+#define        AR9170_PTA_REG_MEM_ADDR                 (AR9170_PTA_REG_BASE + 0x128)
+#define        AR9170_PTA_REG_DN_DMA_TRIGGER           (AR9170_PTA_REG_BASE + 0x12c)
+#define        AR9170_PTA_REG_UP_DMA_TRIGGER           (AR9170_PTA_REG_BASE + 0x130)
+#define        AR9170_PTA_REG_DMA_STATUS               (AR9170_PTA_REG_BASE + 0x134)
+#define        AR9170_PTA_REG_DN_CURR_ADDRL            (AR9170_PTA_REG_BASE + 0x138)
+#define        AR9170_PTA_REG_DN_CURR_ADDRH            (AR9170_PTA_REG_BASE + 0x13c)
+#define        AR9170_PTA_REG_UP_CURR_ADDRL            (AR9170_PTA_REG_BASE + 0x140)
+#define        AR9170_PTA_REG_UP_CURR_ADDRH            (AR9170_PTA_REG_BASE + 0x144)
+#define        AR9170_PTA_REG_DMA_MODE_CTRL            (AR9170_PTA_REG_BASE + 0x148)
+#define                AR9170_PTA_DMA_MODE_CTRL_RESET          BIT(0)
+#define                AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB    BIT(1)
+
+/* Protocol Controller Module */
+#define        AR9170_MAC_REG_PC_REG_BASE              (AR9170_MAC_REG_BASE + 0xe00)
+
+
+#define        AR9170_NUM_LEDS                         2
+
+/* CAM */
+#define        AR9170_CAM_MAX_USER                     64
+#define        AR9170_CAM_MAX_KEY_LENGTH               16
+
+#define AR9170_SRAM_OFFSET             0x100000
+#define AR9170_SRAM_SIZE               0x18000
+
+#define AR9170_PRAM_OFFSET             0x200000
+#define AR9170_PRAM_SIZE               0x8000
+
+enum cpu_clock {
+       AHB_STATIC_40MHZ = 0,
+       AHB_GMODE_22MHZ = 1,
+       AHB_AMODE_20MHZ = 1,
+       AHB_GMODE_44MHZ = 2,
+       AHB_AMODE_40MHZ = 2,
+       AHB_GMODE_88MHZ = 3,
+       AHB_AMODE_80MHZ = 3
+};
+
+/* USB endpoints */
+enum ar9170_usb_ep {
+       /*
+        * Control EP is always EP 0 (USB SPEC)
+        *
+        * The weird thing is: the original firmware has a few
+        * comments that suggest that the actual EP numbers
+        * are in the 1 to 10 range?!
+        */
+       AR9170_USB_EP_CTRL              = 0,
+
+       AR9170_USB_EP_TX,
+       AR9170_USB_EP_RX,
+       AR9170_USB_EP_IRQ,
+       AR9170_USB_EP_CMD,
+       AR9170_USB_NUM_EXTRA_EP         = 4,
+
+       __AR9170_USB_NUM_EP,
+
+       __AR9170_USB_NUM_MAX_EP         = 10
+};
+
+enum ar9170_usb_fifo {
+       __AR9170_USB_NUM_MAX_FIFO       = 10
+};
+
+enum ar9170_tx_queues {
+       AR9170_TXQ0     = 0,
+       AR9170_TXQ1,
+       AR9170_TXQ2,
+       AR9170_TXQ3,
+       AR9170_TXQ_SPECIAL,
+
+       /* keep last */
+       __AR9170_NUM_TX_QUEUES = 5
+};
+
+#define        AR9170_TX_STREAM_TAG            0x697e
+#define        AR9170_RX_STREAM_TAG            0x4e00
+#define        AR9170_RX_STREAM_MAX_SIZE       0xffff
+
+struct ar9170_stream {
+       __le16 length;
+       __le16 tag;
+
+       u8 payload[0];
+};
+
+#define AR9170_MAX_ACKTABLE_ENTRIES                    8
+#define AR9170_MAX_VIRTUAL_MAC                         7
+
+#define        AR9170_USB_EP_CTRL_MAX                          64
+#define        AR9170_USB_EP_TX_MAX                            512
+#define        AR9170_USB_EP_RX_MAX                            512
+#define        AR9170_USB_EP_IRQ_MAX                           64
+#define        AR9170_USB_EP_CMD_MAX                           64
+
+/* Trigger PRETBTT interrupt 6 Kus earlier */
+#define CARL9170_PRETBTT_KUS                           6
+
+#define        AR5416_MAX_RATE_POWER                           63
+
+#define SET_VAL(reg, value, newvalue)                                  \
+       (value = ((value) & ~reg) | (((newvalue) << reg##_S) & reg))
+
+#define MOD_VAL(reg, value, newvalue)                                  \
+       (((value) & ~reg) | (((newvalue) << reg##_S) & reg))
+#endif /* __CARL9170_SHARED_HW_H */
diff --git a/drivers/net/wireless/ath/carl9170/led.c b/drivers/net/wireless/ath/carl9170/led.c
new file mode 100644 (file)
index 0000000..4bb2cbd
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * LED handling
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparer <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 "carl9170.h"
+#include "cmd.h"
+
+int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state)
+{
+       return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state);
+}
+
+int carl9170_led_init(struct ar9170 *ar)
+{
+       int err;
+
+       /* disable LEDs */
+       /* GPIO [0/1 mode: output, 2/3: input] */
+       err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
+       if (err)
+               goto out;
+
+       /* GPIO 0/1 value: off */
+       err = carl9170_led_set_state(ar, 0);
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_CARL9170_LEDS
+static void carl9170_led_update(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
+       int i, tmp = 300, blink_delay = 1000;
+       u32 led_val = 0;
+       bool rerun = false;
+
+       if (!IS_ACCEPTING_CMD(ar))
+               return;
+
+       mutex_lock(&ar->mutex);
+       for (i = 0; i < AR9170_NUM_LEDS; i++) {
+               if (ar->leds[i].registered) {
+                       if (ar->leds[i].last_state ||
+                           ar->leds[i].toggled) {
+
+                               if (ar->leds[i].toggled)
+                                       tmp = 70 + 200 / (ar->leds[i].toggled);
+
+                               if (tmp < blink_delay)
+                                       blink_delay = tmp;
+
+                               led_val |= 1 << i;
+                               ar->leds[i].toggled = 0;
+                               rerun = true;
+                       }
+               }
+       }
+
+       carl9170_led_set_state(ar, led_val);
+       mutex_unlock(&ar->mutex);
+
+       if (!rerun)
+               return;
+
+       ieee80211_queue_delayed_work(ar->hw,
+                                    &ar->led_work,
+                                    msecs_to_jiffies(blink_delay));
+}
+
+static void carl9170_led_set_brightness(struct led_classdev *led,
+                                       enum led_brightness brightness)
+{
+       struct carl9170_led *arl = container_of(led, struct carl9170_led, l);
+       struct ar9170 *ar = arl->ar;
+
+       if (!arl->registered)
+               return;
+
+       if (arl->last_state != !!brightness) {
+               arl->toggled++;
+               arl->last_state = !!brightness;
+       }
+
+       if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
+               ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10);
+}
+
+static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name,
+                                    char *trigger)
+{
+       int err;
+
+       snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
+                "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
+
+       ar->leds[i].ar = ar;
+       ar->leds[i].l.name = ar->leds[i].name;
+       ar->leds[i].l.brightness_set = carl9170_led_set_brightness;
+       ar->leds[i].l.brightness = 0;
+       ar->leds[i].l.default_trigger = trigger;
+
+       err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
+                                   &ar->leds[i].l);
+       if (err) {
+               wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n",
+                       ar->leds[i].name, err);
+       } else {
+               ar->leds[i].registered = true;
+       }
+
+       return err;
+}
+
+void carl9170_led_unregister(struct ar9170 *ar)
+{
+       int i;
+
+       for (i = 0; i < AR9170_NUM_LEDS; i++)
+               if (ar->leds[i].registered) {
+                       led_classdev_unregister(&ar->leds[i].l);
+                       ar->leds[i].registered = false;
+                       ar->leds[i].toggled = 0;
+               }
+
+       cancel_delayed_work_sync(&ar->led_work);
+}
+
+int carl9170_led_register(struct ar9170 *ar)
+{
+       int err;
+
+       INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update);
+
+       err = carl9170_led_register_led(ar, 0, "tx",
+                                       ieee80211_get_tx_led_name(ar->hw));
+       if (err)
+               goto fail;
+
+       if (ar->features & CARL9170_ONE_LED)
+               return 0;
+
+       err = carl9170_led_register_led(ar, 1, "assoc",
+                                       ieee80211_get_assoc_led_name(ar->hw));
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       carl9170_led_unregister(ar);
+       return err;
+}
+
+#endif /* CONFIG_CARL9170_LEDS */
diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c
new file mode 100644 (file)
index 0000000..2305bc2
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * MAC programming
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 <asm/unaligned.h>
+
+#include "carl9170.h"
+#include "cmd.h"
+
+int carl9170_set_dyn_sifs_ack(struct ar9170 *ar)
+{
+       u32 val;
+
+       if (conf_is_ht40(&ar->hw->conf))
+               val = 0x010a;
+       else {
+               if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+                       val = 0x105;
+               else
+                       val = 0x104;
+       }
+
+       return carl9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val);
+}
+
+int carl9170_set_rts_cts_rate(struct ar9170 *ar)
+{
+       u32 rts_rate, cts_rate;
+
+       if (conf_is_ht(&ar->hw->conf)) {
+               /* 12 mbit OFDM */
+               rts_rate = 0x1da;
+               cts_rate = 0x10a;
+       } else {
+               if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+                       /* 11 mbit CCK */
+                       rts_rate = 033;
+                       cts_rate = 003;
+               } else {
+                       /* 6 mbit OFDM */
+                       rts_rate = 0x1bb;
+                       cts_rate = 0x10b;
+               }
+       }
+
+       return carl9170_write_reg(ar, AR9170_MAC_REG_RTS_CTS_RATE,
+                                 rts_rate | (cts_rate) << 16);
+}
+
+int carl9170_set_slot_time(struct ar9170 *ar)
+{
+       struct ieee80211_vif *vif;
+       u32 slottime = 20;
+
+       rcu_read_lock();
+       vif = carl9170_get_main_vif(ar);
+       if (!vif) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) ||
+           vif->bss_conf.use_short_slot)
+               slottime = 9;
+
+       rcu_read_unlock();
+
+       return carl9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME,
+                                 slottime << 10);
+}
+
+int carl9170_set_mac_rates(struct ar9170 *ar)
+{
+       struct ieee80211_vif *vif;
+       u32 basic, mandatory;
+
+       rcu_read_lock();
+       vif = carl9170_get_main_vif(ar);
+
+       if (!vif) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       basic = (vif->bss_conf.basic_rates & 0xf);
+       basic |= (vif->bss_conf.basic_rates & 0xff0) << 4;
+       rcu_read_unlock();
+
+       if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+               mandatory = 0xff00; /* OFDM 6/9/12/18/24/36/48/54 */
+       else
+               mandatory = 0xff0f; /* OFDM (6/9../54) + CCK (1/2/5.5/11) */
+
+       carl9170_regwrite_begin(ar);
+       carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, basic);
+       carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, mandatory);
+       carl9170_regwrite_finish();
+
+       return carl9170_regwrite_result();
+}
+
+int carl9170_set_qos(struct ar9170 *ar)
+{
+       carl9170_regwrite_begin(ar);
+
+       carl9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
+                         (ar->edcf[0].cw_max << 16));
+       carl9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
+                         (ar->edcf[1].cw_max << 16));
+       carl9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
+                         (ar->edcf[2].cw_max << 16));
+       carl9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
+                         (ar->edcf[3].cw_max << 16));
+       carl9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
+                         (ar->edcf[4].cw_max << 16));
+
+       carl9170_regwrite(AR9170_MAC_REG_AC2_AC1_AC0_AIFS,
+                         ((ar->edcf[0].aifs * 9 + 10)) |
+                         ((ar->edcf[1].aifs * 9 + 10) << 12) |
+                         ((ar->edcf[2].aifs * 9 + 10) << 24));
+       carl9170_regwrite(AR9170_MAC_REG_AC4_AC3_AC2_AIFS,
+                         ((ar->edcf[2].aifs * 9 + 10) >> 8) |
+                         ((ar->edcf[3].aifs * 9 + 10) << 4) |
+                         ((ar->edcf[4].aifs * 9 + 10) << 16));
+
+       carl9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
+                         ar->edcf[0].txop | ar->edcf[1].txop << 16);
+       carl9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
+                         ar->edcf[2].txop | ar->edcf[3].txop << 16 |
+                         ar->edcf[4].txop << 24);
+
+       carl9170_regwrite_finish();
+
+       return carl9170_regwrite_result();
+}
+
+int carl9170_init_mac(struct ar9170 *ar)
+{
+       carl9170_regwrite_begin(ar);
+
+       /* switch MAC to OTUS interface */
+       carl9170_regwrite(0x1c3600, 0x3);
+
+       carl9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
+
+       carl9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0x0);
+
+       carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+                         AR9170_MAC_FTF_MONITOR);
+
+       /* enable MMIC */
+       carl9170_regwrite(AR9170_MAC_REG_SNIFFER,
+                       AR9170_MAC_SNIFFER_DEFAULTS);
+
+       carl9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
+
+       carl9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
+       carl9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
+       carl9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
+
+       /* CF-END & CF-ACK rate => 24M OFDM */
+       carl9170_regwrite(AR9170_MAC_REG_TID_CFACK_CFEND_RATE, 0x59900000);
+
+       /* NAV protects ACK only (in TXOP) */
+       carl9170_regwrite(AR9170_MAC_REG_TXOP_DURATION, 0x201);
+
+       /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
+       /* OTUS set AM to 0x1 */
+       carl9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
+
+       carl9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
+
+       /* Aggregation MAX number and timeout */
+       carl9170_regwrite(AR9170_MAC_REG_AMPDU_FACTOR, 0xa);
+       carl9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, 0x140a00);
+
+       carl9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+                         AR9170_MAC_FTF_DEFAULTS);
+
+       carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL,
+                         AR9170_MAC_RX_CTRL_DEAGG |
+                         AR9170_MAC_RX_CTRL_SHORT_FILTER);
+
+       /* rate sets */
+       carl9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
+       carl9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
+       carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x0030033);
+
+       /* MIMO response control */
+       carl9170_regwrite(AR9170_MAC_REG_ACK_TPC, 0x4003c1e);
+
+       carl9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
+
+       /* set PHY register read timeout (??) */
+       carl9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
+
+       /* Disable Rx TimeOut, workaround for BB. */
+       carl9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
+
+       /* Set WLAN DMA interrupt mode: generate int per packet */
+       carl9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
+
+       carl9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
+                       AR9170_MAC_FCS_FIFO_PROT);
+
+       /* Disables the CF_END frame, undocumented register */
+       carl9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
+                       0x141e0f48);
+
+       /* reset group hash table */
+       carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, 0xffffffff);
+       carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, 0xffffffff);
+
+       /* disable PRETBTT interrupt */
+       carl9170_regwrite(AR9170_MAC_REG_PRETBTT, 0x0);
+       carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, 0x0);
+
+       carl9170_regwrite_finish();
+
+       return carl9170_regwrite_result();
+}
+
+static int carl9170_set_mac_reg(struct ar9170 *ar,
+                               const u32 reg, const u8 *mac)
+{
+       static const u8 zero[ETH_ALEN] = { 0 };
+
+       if (!mac)
+               mac = zero;
+
+       carl9170_regwrite_begin(ar);
+
+       carl9170_regwrite(reg, get_unaligned_le32(mac));
+       carl9170_regwrite(reg + 4, get_unaligned_le16(mac + 4));
+
+       carl9170_regwrite_finish();
+
+       return carl9170_regwrite_result();
+}
+
+int carl9170_mod_virtual_mac(struct ar9170 *ar, const unsigned int id,
+                            const u8 *mac)
+{
+       if (WARN_ON(id >= ar->fw.vif_num))
+               return -EINVAL;
+
+       return carl9170_set_mac_reg(ar,
+               AR9170_MAC_REG_ACK_TABLE + (id - 1) * 8, mac);
+}
+
+int carl9170_update_multicast(struct ar9170 *ar, const u64 mc_hash)
+{
+       int err;
+
+       carl9170_regwrite_begin(ar);
+       carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, mc_hash >> 32);
+       carl9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, mc_hash);
+       carl9170_regwrite_finish();
+       err = carl9170_regwrite_result();
+       if (err)
+               return err;
+
+       ar->cur_mc_hash = mc_hash;
+       return 0;
+}
+
+int carl9170_set_operating_mode(struct ar9170 *ar)
+{
+       struct ieee80211_vif *vif;
+       struct ath_common *common = &ar->common;
+       u8 *mac_addr, *bssid;
+       u32 cam_mode = AR9170_MAC_CAM_DEFAULTS;
+       u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS;
+       u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG |
+                     AR9170_MAC_RX_CTRL_SHORT_FILTER;
+       u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS;
+       int err = 0;
+
+       rcu_read_lock();
+       vif = carl9170_get_main_vif(ar);
+
+       if (vif) {
+               mac_addr = common->macaddr;
+               bssid = common->curbssid;
+
+               switch (vif->type) {
+               case NL80211_IFTYPE_MESH_POINT:
+               case NL80211_IFTYPE_ADHOC:
+                       cam_mode |= AR9170_MAC_CAM_IBSS;
+                       break;
+               case NL80211_IFTYPE_AP:
+                       cam_mode |= AR9170_MAC_CAM_AP;
+
+                       /* iwlagn 802.11n STA Workaround */
+                       rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+                       break;
+               case NL80211_IFTYPE_WDS:
+                       cam_mode |= AR9170_MAC_CAM_AP_WDS;
+                       rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+                       break;
+               case NL80211_IFTYPE_STATION:
+                       cam_mode |= AR9170_MAC_CAM_STA;
+                       rx_ctrl |= AR9170_MAC_RX_CTRL_PASS_TO_HOST;
+                       break;
+               default:
+                       WARN(1, "Unsupported operation mode %x\n", vif->type);
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+       } else {
+               mac_addr = NULL;
+               bssid = NULL;
+       }
+       rcu_read_unlock();
+
+       if (err)
+               return err;
+
+       if (ar->rx_software_decryption)
+               enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE;
+
+       if (ar->sniffer_enabled) {
+               rx_ctrl |= AR9170_MAC_RX_CTRL_ACK_IN_SNIFFER;
+               sniffer |= AR9170_MAC_SNIFFER_ENABLE_PROMISC;
+               enc_mode |= AR9170_MAC_ENCRYPTION_RX_SOFTWARE;
+       }
+
+       err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
+       if (err)
+               return err;
+
+       err = carl9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
+       if (err)
+               return err;
+
+       carl9170_regwrite_begin(ar);
+       carl9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
+       carl9170_regwrite(AR9170_MAC_REG_CAM_MODE, cam_mode);
+       carl9170_regwrite(AR9170_MAC_REG_ENCRYPTION, enc_mode);
+       carl9170_regwrite(AR9170_MAC_REG_RX_CONTROL, rx_ctrl);
+       carl9170_regwrite_finish();
+
+       return carl9170_regwrite_result();
+}
+
+int carl9170_set_hwretry_limit(struct ar9170 *ar, const unsigned int max_retry)
+{
+       u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
+
+       return carl9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
+}
+
+int carl9170_set_beacon_timers(struct ar9170 *ar)
+{
+       struct ieee80211_vif *vif;
+       u32 v = 0;
+       u32 pretbtt = 0;
+
+       rcu_read_lock();
+       vif = carl9170_get_main_vif(ar);
+
+       if (vif) {
+               struct carl9170_vif_info *mvif;
+               mvif = (void *) vif->drv_priv;
+
+               if (mvif->enable_beacon && !WARN_ON(!ar->beacon_enabled)) {
+                       ar->global_beacon_int = vif->bss_conf.beacon_int /
+                                               ar->beacon_enabled;
+
+                       SET_VAL(AR9170_MAC_BCN_DTIM, v,
+                               vif->bss_conf.dtim_period);
+
+                       switch (vif->type) {
+                       case NL80211_IFTYPE_MESH_POINT:
+                       case NL80211_IFTYPE_ADHOC:
+                               v |= AR9170_MAC_BCN_IBSS_MODE;
+                               break;
+                       case NL80211_IFTYPE_AP:
+                               v |= AR9170_MAC_BCN_AP_MODE;
+                               break;
+                       default:
+                               WARN_ON_ONCE(1);
+                               break;
+                       }
+               } else if (vif->type == NL80211_IFTYPE_STATION) {
+                       ar->global_beacon_int = vif->bss_conf.beacon_int;
+
+                       SET_VAL(AR9170_MAC_BCN_DTIM, v,
+                               ar->hw->conf.ps_dtim_period);
+
+                       v |= AR9170_MAC_BCN_STA_PS |
+                            AR9170_MAC_BCN_PWR_MGT;
+               }
+
+               if (ar->global_beacon_int) {
+                       if (ar->global_beacon_int < 15) {
+                               rcu_read_unlock();
+                               return -ERANGE;
+                       }
+
+                       ar->global_pretbtt = ar->global_beacon_int -
+                                       CARL9170_PRETBTT_KUS;
+               } else {
+                       ar->global_pretbtt = 0;
+               }
+       } else {
+               ar->global_beacon_int = 0;
+               ar->global_pretbtt = 0;
+       }
+
+       rcu_read_unlock();
+
+       SET_VAL(AR9170_MAC_BCN_PERIOD, v, ar->global_beacon_int);
+       SET_VAL(AR9170_MAC_PRETBTT, pretbtt, ar->global_pretbtt);
+       SET_VAL(AR9170_MAC_PRETBTT2, pretbtt, ar->global_pretbtt);
+
+       carl9170_regwrite_begin(ar);
+       carl9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+       carl9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+       carl9170_regwrite_finish();
+       return carl9170_regwrite_result();
+}
+
+int carl9170_update_beacon(struct ar9170 *ar, const bool submit)
+{
+       struct sk_buff *skb;
+       struct carl9170_vif_info *cvif;
+       __le32 *data, *old = NULL;
+       u32 word, off, addr, len;
+       int i = 0, err = 0;
+
+       rcu_read_lock();
+       cvif = rcu_dereference(ar->beacon_iter);
+retry:
+       if (ar->vifs == 0 || !cvif)
+               goto out_unlock;
+
+       list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) {
+               if (cvif->active && cvif->enable_beacon)
+                       goto found;
+       }
+
+       if (!ar->beacon_enabled || i++)
+               goto out_unlock;
+
+       goto retry;
+
+found:
+       rcu_assign_pointer(ar->beacon_iter, cvif);
+
+       skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif),
+               NULL, NULL);
+
+       if (!skb) {
+               err = -ENOMEM;
+               goto out_unlock;
+       }
+
+       spin_lock_bh(&ar->beacon_lock);
+       data = (__le32 *)skb->data;
+       if (cvif->beacon)
+               old = (__le32 *)cvif->beacon->data;
+
+       off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX;
+       addr = ar->fw.beacon_addr + off;
+       len = roundup(skb->len + FCS_LEN, 4);
+
+       if ((off + len) > ar->fw.beacon_max_len) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "beacon does not "
+                                 "fit into device memory!\n");
+               }
+
+               spin_unlock_bh(&ar->beacon_lock);
+               dev_kfree_skb_any(skb);
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (len > AR9170_MAC_BCN_LENGTH_MAX) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "no support for beacons "
+                               "bigger than %d (yours:%d).\n",
+                                AR9170_MAC_BCN_LENGTH_MAX, len);
+               }
+
+               spin_unlock_bh(&ar->beacon_lock);
+               dev_kfree_skb_any(skb);
+               err = -EMSGSIZE;
+               goto out_unlock;
+       }
+
+       carl9170_async_regwrite_begin(ar);
+
+       /* XXX: use skb->cb info */
+       if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+               carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP,
+                               ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400);
+       } else {
+               carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP,
+                               ((skb->len + FCS_LEN) << 16) + 0x001b);
+       }
+
+       for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+               /*
+                * XXX: This accesses beyond skb data for up
+                *      to the last 3 bytes!!
+                */
+
+               if (old && (data[i] == old[i]))
+                       continue;
+
+               word = le32_to_cpu(data[i]);
+               carl9170_async_regwrite(addr + 4 * i, word);
+       }
+       carl9170_async_regwrite_finish();
+
+       dev_kfree_skb_any(cvif->beacon);
+       cvif->beacon = NULL;
+
+       err = carl9170_async_regwrite_result();
+       if (!err)
+               cvif->beacon = skb;
+       spin_unlock_bh(&ar->beacon_lock);
+       if (err)
+               goto out_unlock;
+
+       if (submit) {
+               err = carl9170_bcn_ctrl(ar, cvif->id,
+                                       CARL9170_BCN_CTRL_CAB_TRIGGER,
+                                       addr, skb->len + FCS_LEN);
+
+               if (err)
+                       goto out_unlock;
+       }
+out_unlock:
+       rcu_read_unlock();
+       return err;
+}
+
+int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac,
+                       const u8 ktype, const u8 keyidx, const u8 *keydata,
+                       const int keylen)
+{
+       struct carl9170_set_key_cmd key = { };
+       static const u8 bcast[ETH_ALEN] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+       mac = mac ? : bcast;
+
+       key.user = cpu_to_le16(id);
+       key.keyId = cpu_to_le16(keyidx);
+       key.type = cpu_to_le16(ktype);
+       memcpy(&key.macAddr, mac, ETH_ALEN);
+       if (keydata)
+               memcpy(&key.key, keydata, keylen);
+
+       return carl9170_exec_cmd(ar, CARL9170_CMD_EKEY,
+               sizeof(key), (u8 *)&key, 0, NULL);
+}
+
+int carl9170_disable_key(struct ar9170 *ar, const u8 id)
+{
+       struct carl9170_disable_key_cmd key = { };
+
+       key.user = cpu_to_le16(id);
+
+       return carl9170_exec_cmd(ar, CARL9170_CMD_DKEY,
+               sizeof(key), (u8 *)&key, 0, NULL);
+}
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
new file mode 100644 (file)
index 0000000..43de9df
--- /dev/null
@@ -0,0 +1,1855 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * mac80211 interaction code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include "hw.h"
+#include "carl9170.h"
+#include "cmd.h"
+
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware crypto offload.");
+
+int modparam_noht;
+module_param_named(noht, modparam_noht, int, S_IRUGO);
+MODULE_PARM_DESC(noht, "Disable MPDU aggregation.");
+
+#define RATE(_bitrate, _hw_rate, _txpidx, _flags) {    \
+       .bitrate        = (_bitrate),                   \
+       .flags          = (_flags),                     \
+       .hw_value       = (_hw_rate) | (_txpidx) << 4,  \
+}
+
+struct ieee80211_rate __carl9170_ratetable[] = {
+       RATE(10, 0, 0, 0),
+       RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(60, 0xb, 0, 0),
+       RATE(90, 0xf, 0, 0),
+       RATE(120, 0xa, 0, 0),
+       RATE(180, 0xe, 0, 0),
+       RATE(240, 0x9, 0, 0),
+       RATE(360, 0xd, 1, 0),
+       RATE(480, 0x8, 2, 0),
+       RATE(540, 0xc, 3, 0),
+};
+#undef RATE
+
+#define carl9170_g_ratetable   (__carl9170_ratetable + 0)
+#define carl9170_g_ratetable_size      12
+#define carl9170_a_ratetable   (__carl9170_ratetable + 4)
+#define carl9170_a_ratetable_size      8
+
+/*
+ * NB: The hw_value is used as an index into the carl9170_phy_freq_params
+ *     array in phy.c so that we don't have to do frequency lookups!
+ */
+#define CHAN(_freq, _idx) {            \
+       .center_freq    = (_freq),      \
+       .hw_value       = (_idx),       \
+       .max_power      = 18, /* XXX */ \
+}
+
+static struct ieee80211_channel carl9170_2ghz_chantable[] = {
+       CHAN(2412,  0),
+       CHAN(2417,  1),
+       CHAN(2422,  2),
+       CHAN(2427,  3),
+       CHAN(2432,  4),
+       CHAN(2437,  5),
+       CHAN(2442,  6),
+       CHAN(2447,  7),
+       CHAN(2452,  8),
+       CHAN(2457,  9),
+       CHAN(2462, 10),
+       CHAN(2467, 11),
+       CHAN(2472, 12),
+       CHAN(2484, 13),
+};
+
+static struct ieee80211_channel carl9170_5ghz_chantable[] = {
+       CHAN(4920, 14),
+       CHAN(4940, 15),
+       CHAN(4960, 16),
+       CHAN(4980, 17),
+       CHAN(5040, 18),
+       CHAN(5060, 19),
+       CHAN(5080, 20),
+       CHAN(5180, 21),
+       CHAN(5200, 22),
+       CHAN(5220, 23),
+       CHAN(5240, 24),
+       CHAN(5260, 25),
+       CHAN(5280, 26),
+       CHAN(5300, 27),
+       CHAN(5320, 28),
+       CHAN(5500, 29),
+       CHAN(5520, 30),
+       CHAN(5540, 31),
+       CHAN(5560, 32),
+       CHAN(5580, 33),
+       CHAN(5600, 34),
+       CHAN(5620, 35),
+       CHAN(5640, 36),
+       CHAN(5660, 37),
+       CHAN(5680, 38),
+       CHAN(5700, 39),
+       CHAN(5745, 40),
+       CHAN(5765, 41),
+       CHAN(5785, 42),
+       CHAN(5805, 43),
+       CHAN(5825, 44),
+       CHAN(5170, 45),
+       CHAN(5190, 46),
+       CHAN(5210, 47),
+       CHAN(5230, 48),
+};
+#undef CHAN
+
+#define CARL9170_HT_CAP                                                        \
+{                                                                      \
+       .ht_supported   = true,                                         \
+       .cap            = IEEE80211_HT_CAP_MAX_AMSDU |                  \
+                         IEEE80211_HT_CAP_SUP_WIDTH_20_40 |            \
+                         IEEE80211_HT_CAP_SGI_40 |                     \
+                         IEEE80211_HT_CAP_DSSSCCK40 |                  \
+                         IEEE80211_HT_CAP_SM_PS,                       \
+       .ampdu_factor   = IEEE80211_HT_MAX_AMPDU_64K,                   \
+       .ampdu_density  = IEEE80211_HT_MPDU_DENSITY_8,                  \
+       .mcs            = {                                             \
+               .rx_mask = { 0xff, 0xff, 0, 0, 0x1, 0, 0, 0, 0, 0, },   \
+               .rx_highest = cpu_to_le16(300),                         \
+               .tx_params = IEEE80211_HT_MCS_TX_DEFINED,               \
+       },                                                              \
+}
+
+static struct ieee80211_supported_band carl9170_band_2GHz = {
+       .channels       = carl9170_2ghz_chantable,
+       .n_channels     = ARRAY_SIZE(carl9170_2ghz_chantable),
+       .bitrates       = carl9170_g_ratetable,
+       .n_bitrates     = carl9170_g_ratetable_size,
+       .ht_cap         = CARL9170_HT_CAP,
+};
+
+static struct ieee80211_supported_band carl9170_band_5GHz = {
+       .channels       = carl9170_5ghz_chantable,
+       .n_channels     = ARRAY_SIZE(carl9170_5ghz_chantable),
+       .bitrates       = carl9170_a_ratetable,
+       .n_bitrates     = carl9170_a_ratetable_size,
+       .ht_cap         = CARL9170_HT_CAP,
+};
+
+static void carl9170_ampdu_gc(struct ar9170 *ar)
+{
+       struct carl9170_sta_tid *tid_info;
+       LIST_HEAD(tid_gc);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) {
+               spin_lock_bh(&ar->tx_ampdu_list_lock);
+               if (tid_info->state == CARL9170_TID_STATE_SHUTDOWN) {
+                       tid_info->state = CARL9170_TID_STATE_KILLED;
+                       list_del_rcu(&tid_info->list);
+                       ar->tx_ampdu_list_len--;
+                       list_add_tail(&tid_info->tmp_list, &tid_gc);
+               }
+               spin_unlock_bh(&ar->tx_ampdu_list_lock);
+
+       }
+       rcu_assign_pointer(ar->tx_ampdu_iter, tid_info);
+       rcu_read_unlock();
+
+       synchronize_rcu();
+
+       while (!list_empty(&tid_gc)) {
+               struct sk_buff *skb;
+               tid_info = list_first_entry(&tid_gc, struct carl9170_sta_tid,
+                                           tmp_list);
+
+               while ((skb = __skb_dequeue(&tid_info->queue)))
+                       carl9170_tx_status(ar, skb, false);
+
+               list_del_init(&tid_info->tmp_list);
+               kfree(tid_info);
+       }
+}
+
+static void carl9170_flush(struct ar9170 *ar, bool drop_queued)
+{
+       if (drop_queued) {
+               int i;
+
+               /*
+                * We can only drop frames which have not been uploaded
+                * to the device yet.
+                */
+
+               for (i = 0; i < ar->hw->queues; i++) {
+                       struct sk_buff *skb;
+
+                       while ((skb = skb_dequeue(&ar->tx_pending[i])))
+                               carl9170_tx_status(ar, skb, false);
+               }
+       }
+
+       /* Wait for all other outstanding frames to timeout. */
+       if (atomic_read(&ar->tx_total_queued))
+               WARN_ON(wait_for_completion_timeout(&ar->tx_flush, HZ) == 0);
+}
+
+static void carl9170_flush_ba(struct ar9170 *ar)
+{
+       struct sk_buff_head free;
+       struct carl9170_sta_tid *tid_info;
+       struct sk_buff *skb;
+
+       __skb_queue_head_init(&free);
+
+       rcu_read_lock();
+       spin_lock_bh(&ar->tx_ampdu_list_lock);
+       list_for_each_entry_rcu(tid_info, &ar->tx_ampdu_list, list) {
+               if (tid_info->state > CARL9170_TID_STATE_SUSPEND) {
+                       tid_info->state = CARL9170_TID_STATE_SUSPEND;
+
+                       spin_lock(&tid_info->lock);
+                       while ((skb = __skb_dequeue(&tid_info->queue)))
+                               __skb_queue_tail(&free, skb);
+                       spin_unlock(&tid_info->lock);
+               }
+       }
+       spin_unlock_bh(&ar->tx_ampdu_list_lock);
+       rcu_read_unlock();
+
+       while ((skb = __skb_dequeue(&free)))
+               carl9170_tx_status(ar, skb, false);
+}
+
+static void carl9170_zap_queues(struct ar9170 *ar)
+{
+       struct carl9170_vif_info *cvif;
+       unsigned int i;
+
+       carl9170_ampdu_gc(ar);
+
+       carl9170_flush_ba(ar);
+       carl9170_flush(ar, true);
+
+       for (i = 0; i < ar->hw->queues; i++) {
+               spin_lock_bh(&ar->tx_status[i].lock);
+               while (!skb_queue_empty(&ar->tx_status[i])) {
+                       struct sk_buff *skb;
+
+                       skb = skb_peek(&ar->tx_status[i]);
+                       carl9170_tx_get_skb(skb);
+                       spin_unlock_bh(&ar->tx_status[i].lock);
+                       carl9170_tx_drop(ar, skb);
+                       spin_lock_bh(&ar->tx_status[i].lock);
+                       carl9170_tx_put_skb(skb);
+               }
+               spin_unlock_bh(&ar->tx_status[i].lock);
+       }
+
+       BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_SOFT < 1);
+       BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD < CARL9170_NUM_TX_LIMIT_SOFT);
+       BUILD_BUG_ON(CARL9170_NUM_TX_LIMIT_HARD >= CARL9170_BAW_BITS);
+
+       /* reinitialize queues statistics */
+       memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
+       for (i = 0; i < ar->hw->queues; i++)
+               ar->tx_stats[i].limit = CARL9170_NUM_TX_LIMIT_HARD;
+
+       for (i = 0; i < DIV_ROUND_UP(ar->fw.mem_blocks, BITS_PER_LONG); i++)
+               ar->mem_bitmap[i] = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(cvif, &ar->vif_list, list) {
+               spin_lock_bh(&ar->beacon_lock);
+               dev_kfree_skb_any(cvif->beacon);
+               cvif->beacon = NULL;
+               spin_unlock_bh(&ar->beacon_lock);
+       }
+       rcu_read_unlock();
+
+       atomic_set(&ar->tx_ampdu_upload, 0);
+       atomic_set(&ar->tx_ampdu_scheduler, 0);
+       atomic_set(&ar->tx_total_pending, 0);
+       atomic_set(&ar->tx_total_queued, 0);
+       atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
+}
+
+#define CARL9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop)         \
+do {                                                                   \
+       queue.aifs = ai_fs;                                             \
+       queue.cw_min = cwmin;                                           \
+       queue.cw_max = cwmax;                                           \
+       queue.txop = _txop;                                             \
+} while (0)
+
+static int carl9170_op_start(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+       int err, i;
+
+       mutex_lock(&ar->mutex);
+
+       carl9170_zap_queues(ar);
+
+       /* reset QoS defaults */
+       CARL9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023,  0); /* BEST EFFORT */
+       CARL9170_FILL_QUEUE(ar->edcf[1], 2, 7,    15, 94); /* VIDEO */
+       CARL9170_FILL_QUEUE(ar->edcf[2], 2, 3,     7, 47); /* VOICE */
+       CARL9170_FILL_QUEUE(ar->edcf[3], 7, 15, 1023,  0); /* BACKGROUND */
+       CARL9170_FILL_QUEUE(ar->edcf[4], 2, 3,     7,  0); /* SPECIAL */
+
+       ar->current_factor = ar->current_density = -1;
+       /* "The first key is unique." */
+       ar->usedkeys = 1;
+       ar->filter_state = 0;
+       ar->ps.last_action = jiffies;
+       ar->ps.last_slept = jiffies;
+       ar->erp_mode = CARL9170_ERP_AUTO;
+       ar->rx_software_decryption = false;
+       ar->disable_offload = false;
+
+       for (i = 0; i < ar->hw->queues; i++) {
+               ar->queue_stop_timeout[i] = jiffies;
+               ar->max_queue_stop_timeout[i] = 0;
+       }
+
+       atomic_set(&ar->mem_allocs, 0);
+
+       err = carl9170_usb_open(ar);
+       if (err)
+               goto out;
+
+       err = carl9170_init_mac(ar);
+       if (err)
+               goto out;
+
+       err = carl9170_set_qos(ar);
+       if (err)
+               goto out;
+
+       err = carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER,
+                                AR9170_DMA_TRIGGER_RXQ);
+       if (err)
+               goto out;
+
+       /* Clear key-cache */
+       for (i = 0; i < AR9170_CAM_MAX_USER + 4; i++) {
+               err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE,
+                                         0, NULL, 0);
+               if (err)
+                       goto out;
+
+               err = carl9170_upload_key(ar, i, NULL, AR9170_ENC_ALG_NONE,
+                                         1, NULL, 0);
+               if (err)
+                       goto out;
+
+               if (i < AR9170_CAM_MAX_USER) {
+                       err = carl9170_disable_key(ar, i);
+                       if (err)
+                               goto out;
+               }
+       }
+
+       carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STARTED);
+
+       ieee80211_wake_queues(ar->hw);
+       err = 0;
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static void carl9170_cancel_worker(struct ar9170 *ar)
+{
+       cancel_delayed_work_sync(&ar->tx_janitor);
+#ifdef CONFIG_CARL9170_LEDS
+       cancel_delayed_work_sync(&ar->led_work);
+#endif /* CONFIG_CARL9170_LEDS */
+       cancel_work_sync(&ar->ps_work);
+       cancel_work_sync(&ar->ampdu_work);
+}
+
+static void carl9170_op_stop(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+
+       carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
+
+       ieee80211_stop_queues(ar->hw);
+
+       mutex_lock(&ar->mutex);
+       if (IS_ACCEPTING_CMD(ar)) {
+               rcu_assign_pointer(ar->beacon_iter, NULL);
+
+               carl9170_led_set_state(ar, 0);
+
+               /* stop DMA */
+               carl9170_write_reg(ar, AR9170_MAC_REG_DMA_TRIGGER, 0);
+               carl9170_usb_stop(ar);
+       }
+
+       carl9170_zap_queues(ar);
+       mutex_unlock(&ar->mutex);
+
+       carl9170_cancel_worker(ar);
+}
+
+static void carl9170_restart_work(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        restart_work);
+       int err;
+
+       ar->usedkeys = 0;
+       ar->filter_state = 0;
+       carl9170_cancel_worker(ar);
+
+       mutex_lock(&ar->mutex);
+       err = carl9170_usb_restart(ar);
+       if (net_ratelimit()) {
+               if (err) {
+                       dev_err(&ar->udev->dev, "Failed to restart device "
+                               " (%d).\n", err);
+                } else {
+                       dev_info(&ar->udev->dev, "device restarted "
+                                "successfully.\n");
+               }
+       }
+
+       carl9170_zap_queues(ar);
+       mutex_unlock(&ar->mutex);
+       if (!err) {
+               ar->restart_counter++;
+               atomic_set(&ar->pending_restarts, 0);
+
+               ieee80211_restart_hw(ar->hw);
+       } else {
+               /*
+                * The reset was unsuccessful and the device seems to
+                * be dead. But there's still one option: a low-level
+                * usb subsystem reset...
+                */
+
+               carl9170_usb_reset(ar);
+       }
+}
+
+void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
+{
+       carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
+
+       /*
+        * Sometimes, an error can trigger several different reset events.
+        * By ignoring these *surplus* reset events, the device won't be
+        * killed again, right after it has recovered.
+        */
+       if (atomic_inc_return(&ar->pending_restarts) > 1) {
+               dev_dbg(&ar->udev->dev, "ignoring restart (%d)\n", r);
+               return;
+       }
+
+       ieee80211_stop_queues(ar->hw);
+
+       dev_err(&ar->udev->dev, "restart device (%d)\n", r);
+
+       if (!WARN_ON(r == CARL9170_RR_NO_REASON) ||
+           !WARN_ON(r >= __CARL9170_RR_LAST))
+               ar->last_reason = r;
+
+       if (!ar->registered)
+               return;
+
+       if (IS_ACCEPTING_CMD(ar) && !ar->needs_full_reset)
+               ieee80211_queue_work(ar->hw, &ar->restart_work);
+       else
+               carl9170_usb_reset(ar);
+
+       /*
+        * At this point, the device instance might have vanished/disabled.
+        * So, don't put any code which access the ar9170 struct
+        * without proper protection.
+        */
+}
+
+static int carl9170_init_interface(struct ar9170 *ar,
+                                  struct ieee80211_vif *vif)
+{
+       struct ath_common *common = &ar->common;
+       int err;
+
+       if (!vif) {
+               WARN_ON_ONCE(IS_STARTED(ar));
+               return 0;
+       }
+
+       memcpy(common->macaddr, vif->addr, ETH_ALEN);
+
+       if (modparam_nohwcrypt ||
+           ((vif->type != NL80211_IFTYPE_STATION) &&
+            (vif->type != NL80211_IFTYPE_AP))) {
+               ar->rx_software_decryption = true;
+               ar->disable_offload = true;
+       }
+
+       err = carl9170_set_operating_mode(ar);
+       return err;
+}
+
+static int carl9170_op_add_interface(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
+{
+       struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv;
+       struct ieee80211_vif *main_vif;
+       struct ar9170 *ar = hw->priv;
+       int vif_id = -1, err = 0;
+
+       mutex_lock(&ar->mutex);
+       rcu_read_lock();
+       if (vif_priv->active) {
+               /*
+                * Skip the interface structure initialization,
+                * if the vif survived the _restart call.
+                */
+               vif_id = vif_priv->id;
+               vif_priv->enable_beacon = false;
+
+               spin_lock_bh(&ar->beacon_lock);
+               dev_kfree_skb_any(vif_priv->beacon);
+               vif_priv->beacon = NULL;
+               spin_unlock_bh(&ar->beacon_lock);
+
+               goto init;
+       }
+
+       main_vif = carl9170_get_main_vif(ar);
+
+       if (main_vif) {
+               switch (main_vif->type) {
+               case NL80211_IFTYPE_STATION:
+                       if (vif->type == NL80211_IFTYPE_STATION)
+                               break;
+
+                       err = -EBUSY;
+                       rcu_read_unlock();
+
+                       goto unlock;
+
+               case NL80211_IFTYPE_AP:
+                       if ((vif->type == NL80211_IFTYPE_STATION) ||
+                           (vif->type == NL80211_IFTYPE_WDS) ||
+                           (vif->type == NL80211_IFTYPE_AP))
+                               break;
+
+                       err = -EBUSY;
+                       rcu_read_unlock();
+                       goto unlock;
+
+               default:
+                       rcu_read_unlock();
+                       goto unlock;
+               }
+       }
+
+       vif_id = bitmap_find_free_region(&ar->vif_bitmap, ar->fw.vif_num, 0);
+
+       if (vif_id < 0) {
+               rcu_read_unlock();
+
+               err = -ENOSPC;
+               goto unlock;
+       }
+
+       BUG_ON(ar->vif_priv[vif_id].id != vif_id);
+
+       vif_priv->active = true;
+       vif_priv->id = vif_id;
+       vif_priv->enable_beacon = false;
+       ar->vifs++;
+       list_add_tail_rcu(&vif_priv->list, &ar->vif_list);
+       rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif);
+
+init:
+       if (carl9170_get_main_vif(ar) == vif) {
+               rcu_assign_pointer(ar->beacon_iter, vif_priv);
+               rcu_read_unlock();
+
+               err = carl9170_init_interface(ar, vif);
+               if (err)
+                       goto unlock;
+       } else {
+               err = carl9170_mod_virtual_mac(ar, vif_id, vif->addr);
+               rcu_read_unlock();
+
+               if (err)
+                       goto unlock;
+       }
+
+unlock:
+       if (err && (vif_id != -1)) {
+               vif_priv->active = false;
+               bitmap_release_region(&ar->vif_bitmap, vif_id, 0);
+               ar->vifs--;
+               rcu_assign_pointer(ar->vif_priv[vif_id].vif, NULL);
+               list_del_rcu(&vif_priv->list);
+               mutex_unlock(&ar->mutex);
+               synchronize_rcu();
+       } else {
+               if (ar->vifs > 1)
+                       ar->ps.off_override |= PS_OFF_VIF;
+
+               mutex_unlock(&ar->mutex);
+       }
+
+       return err;
+}
+
+static void carl9170_op_remove_interface(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif)
+{
+       struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv;
+       struct ieee80211_vif *main_vif;
+       struct ar9170 *ar = hw->priv;
+       unsigned int id;
+
+       mutex_lock(&ar->mutex);
+
+       if (WARN_ON_ONCE(!vif_priv->active))
+               goto unlock;
+
+       ar->vifs--;
+
+       rcu_read_lock();
+       main_vif = carl9170_get_main_vif(ar);
+
+       id = vif_priv->id;
+
+       vif_priv->active = false;
+       WARN_ON(vif_priv->enable_beacon);
+       vif_priv->enable_beacon = false;
+       list_del_rcu(&vif_priv->list);
+       rcu_assign_pointer(ar->vif_priv[id].vif, NULL);
+
+       if (vif == main_vif) {
+               rcu_read_unlock();
+
+               if (ar->vifs) {
+                       WARN_ON(carl9170_init_interface(ar,
+                                       carl9170_get_main_vif(ar)));
+               } else {
+                       carl9170_set_operating_mode(ar);
+               }
+       } else {
+               rcu_read_unlock();
+
+               WARN_ON(carl9170_mod_virtual_mac(ar, id, NULL));
+       }
+
+       carl9170_update_beacon(ar, false);
+       carl9170_flush_cab(ar, id);
+
+       spin_lock_bh(&ar->beacon_lock);
+       dev_kfree_skb_any(vif_priv->beacon);
+       vif_priv->beacon = NULL;
+       spin_unlock_bh(&ar->beacon_lock);
+
+       bitmap_release_region(&ar->vif_bitmap, id, 0);
+
+       carl9170_set_beacon_timers(ar);
+
+       if (ar->vifs == 1)
+               ar->ps.off_override &= ~PS_OFF_VIF;
+
+unlock:
+       mutex_unlock(&ar->mutex);
+
+       synchronize_rcu();
+}
+
+void carl9170_ps_check(struct ar9170 *ar)
+{
+       ieee80211_queue_work(ar->hw, &ar->ps_work);
+}
+
+/* caller must hold ar->mutex */
+static int carl9170_ps_update(struct ar9170 *ar)
+{
+       bool ps = false;
+       int err = 0;
+
+       if (!ar->ps.off_override)
+               ps = (ar->hw->conf.flags & IEEE80211_CONF_PS);
+
+       if (ps != ar->ps.state) {
+               err = carl9170_powersave(ar, ps);
+               if (err)
+                       return err;
+
+               if (ar->ps.state && !ps) {
+                       ar->ps.sleep_ms = jiffies_to_msecs(jiffies -
+                               ar->ps.last_action);
+               }
+
+               if (ps)
+                       ar->ps.last_slept = jiffies;
+
+               ar->ps.last_action = jiffies;
+               ar->ps.state = ps;
+       }
+
+       return 0;
+}
+
+static void carl9170_ps_work(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        ps_work);
+       mutex_lock(&ar->mutex);
+       if (IS_STARTED(ar))
+               WARN_ON_ONCE(carl9170_ps_update(ar) != 0);
+       mutex_unlock(&ar->mutex);
+}
+
+
+static int carl9170_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0;
+
+       mutex_lock(&ar->mutex);
+       if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               err = carl9170_ps_update(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_SMPS) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               /* adjust slot time for 5 GHz */
+               err = carl9170_set_slot_time(ar);
+               if (err)
+                       goto out;
+
+               err = carl9170_set_channel(ar, hw->conf.channel,
+                       hw->conf.channel_type, CARL9170_RFI_NONE);
+               if (err)
+                       goto out;
+
+               err = carl9170_set_dyn_sifs_ack(ar);
+               if (err)
+                       goto out;
+
+               err = carl9170_set_rts_cts_rate(ar);
+               if (err)
+                       goto out;
+       }
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw,
+                                        struct netdev_hw_addr_list *mc_list)
+{
+       struct netdev_hw_addr *ha;
+       u64 mchash;
+
+       /* always get broadcast frames */
+       mchash = 1ULL << (0xff >> 2);
+
+       netdev_hw_addr_list_for_each(ha, mc_list)
+               mchash |= 1ULL << (ha->addr[5] >> 2);
+
+       return mchash;
+}
+
+static void carl9170_op_configure_filter(struct ieee80211_hw *hw,
+                                        unsigned int changed_flags,
+                                        unsigned int *new_flags,
+                                        u64 multicast)
+{
+       struct ar9170 *ar = hw->priv;
+
+       /* mask supported flags */
+       *new_flags &= FIF_ALLMULTI | FIF_FCSFAIL | FIF_PLCPFAIL |
+                     FIF_OTHER_BSS | FIF_PROMISC_IN_BSS;
+
+       if (!IS_ACCEPTING_CMD(ar))
+               return;
+
+       mutex_lock(&ar->mutex);
+
+       ar->filter_state = *new_flags;
+       /*
+        * We can support more by setting the sniffer bit and
+        * then checking the error flags, later.
+        */
+
+       if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI)
+               multicast = ~0ULL;
+
+       if (multicast != ar->cur_mc_hash)
+               WARN_ON(carl9170_update_multicast(ar, multicast));
+
+       if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) {
+               ar->sniffer_enabled = !!(*new_flags &
+                       (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS));
+
+               WARN_ON(carl9170_set_operating_mode(ar));
+       }
+
+       mutex_unlock(&ar->mutex);
+}
+
+
+static void carl9170_op_bss_info_changed(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_bss_conf *bss_conf,
+                                        u32 changed)
+{
+       struct ar9170 *ar = hw->priv;
+       struct ath_common *common = &ar->common;
+       int err = 0;
+       struct carl9170_vif_info *vif_priv;
+       struct ieee80211_vif *main_vif;
+
+       mutex_lock(&ar->mutex);
+       vif_priv = (void *) vif->drv_priv;
+       main_vif = carl9170_get_main_vif(ar);
+       if (WARN_ON(!main_vif))
+               goto out;
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
+               struct carl9170_vif_info *iter;
+               int i = 0;
+
+               vif_priv->enable_beacon = bss_conf->enable_beacon;
+               rcu_read_lock();
+               list_for_each_entry_rcu(iter, &ar->vif_list, list) {
+                       if (iter->active && iter->enable_beacon)
+                               i++;
+
+               }
+               rcu_read_unlock();
+
+               ar->beacon_enabled = i;
+       }
+
+       if (changed & BSS_CHANGED_BEACON) {
+               err = carl9170_update_beacon(ar, false);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON |
+                      BSS_CHANGED_BEACON_INT)) {
+
+               if (main_vif != vif) {
+                       bss_conf->beacon_int = main_vif->bss_conf.beacon_int;
+                       bss_conf->dtim_period = main_vif->bss_conf.dtim_period;
+               }
+
+               /*
+                * Therefore a hard limit for the broadcast traffic should
+                * prevent false alarms.
+                */
+               if (vif->type != NL80211_IFTYPE_STATION &&
+                   (bss_conf->beacon_int * bss_conf->dtim_period >=
+                    (CARL9170_QUEUE_STUCK_TIMEOUT / 2))) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               err = carl9170_set_beacon_timers(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & BSS_CHANGED_HT) {
+               /* TODO */
+               err = 0;
+               if (err)
+                       goto out;
+       }
+
+       if (main_vif != vif)
+               goto out;
+
+       /*
+        * The following settings can only be changed by the
+        * master interface.
+        */
+
+       if (changed & BSS_CHANGED_BSSID) {
+               memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+               err = carl9170_set_operating_mode(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               ar->common.curaid = bss_conf->aid;
+               err = carl9170_set_beacon_timers(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               err = carl9170_set_slot_time(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               err = carl9170_set_mac_rates(ar);
+               if (err)
+                       goto out;
+       }
+
+out:
+       WARN_ON_ONCE(err && IS_STARTED(ar));
+       mutex_unlock(&ar->mutex);
+}
+
+static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+       struct carl9170_tsf_rsp tsf;
+       int err;
+
+       mutex_lock(&ar->mutex);
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_READ_TSF,
+                               0, NULL, sizeof(tsf), &tsf);
+       mutex_unlock(&ar->mutex);
+       if (WARN_ON(err))
+               return 0;
+
+       return le64_to_cpu(tsf.tsf_64);
+}
+
+static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              struct ieee80211_key_conf *key)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0, i;
+       u8 ktype;
+
+       if (ar->disable_offload || !vif)
+               return -EOPNOTSUPP;
+
+       /*
+        * We have to fall back to software encryption, whenever
+        * the user choose to participates in an IBSS or is connected
+        * to more than one network.
+        *
+        * This is very unfortunate, because some machines cannot handle
+        * the high througput speed in 802.11n networks.
+        */
+
+       if (!is_main_vif(ar, vif))
+               goto err_softw;
+
+       /*
+        * While the hardware supports *catch-all* key, for offloading
+        * group-key en-/de-cryption. The way of how the hardware
+        * decides which keyId maps to which key, remains a mystery...
+        */
+       if ((vif->type != NL80211_IFTYPE_STATION &&
+            vif->type != NL80211_IFTYPE_ADHOC) &&
+           !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               return -EOPNOTSUPP;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+               ktype = AR9170_ENC_ALG_WEP64;
+               break;
+       case WLAN_CIPHER_SUITE_WEP104:
+               ktype = AR9170_ENC_ALG_WEP128;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               ktype = AR9170_ENC_ALG_TKIP;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               ktype = AR9170_ENC_ALG_AESCCMP;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&ar->mutex);
+       if (cmd == SET_KEY) {
+               if (!IS_STARTED(ar)) {
+                       err = -EOPNOTSUPP;
+                       goto out;
+               }
+
+               if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+                       sta = NULL;
+
+                       i = 64 + key->keyidx;
+               } else {
+                       for (i = 0; i < 64; i++)
+                               if (!(ar->usedkeys & BIT(i)))
+                                       break;
+                       if (i == 64)
+                               goto err_softw;
+               }
+
+               key->hw_key_idx = i;
+
+               err = carl9170_upload_key(ar, i, sta ? sta->addr : NULL,
+                                         ktype, 0, key->key,
+                                         min_t(u8, 16, key->keylen));
+               if (err)
+                       goto out;
+
+               if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+                       err = carl9170_upload_key(ar, i, sta ? sta->addr :
+                                                 NULL, ktype, 1,
+                                                 key->key + 16, 16);
+                       if (err)
+                               goto out;
+
+                       /*
+                        * hardware is not capable generating MMIC
+                        * of fragmented frames!
+                        */
+                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               }
+
+               if (i < 64)
+                       ar->usedkeys |= BIT(i);
+
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+       } else {
+               if (!IS_STARTED(ar)) {
+                       /* The device is gone... together with the key ;-) */
+                       err = 0;
+                       goto out;
+               }
+
+               if (key->hw_key_idx < 64) {
+                       ar->usedkeys &= ~BIT(key->hw_key_idx);
+               } else {
+                       err = carl9170_upload_key(ar, key->hw_key_idx, NULL,
+                                                 AR9170_ENC_ALG_NONE, 0,
+                                                 NULL, 0);
+                       if (err)
+                               goto out;
+
+                       if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+                               err = carl9170_upload_key(ar, key->hw_key_idx,
+                                                         NULL,
+                                                         AR9170_ENC_ALG_NONE,
+                                                         1, NULL, 0);
+                               if (err)
+                                       goto out;
+                       }
+
+               }
+
+               err = carl9170_disable_key(ar, key->hw_key_idx);
+               if (err)
+                       goto out;
+       }
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+
+err_softw:
+       if (!ar->rx_software_decryption) {
+               ar->rx_software_decryption = true;
+               carl9170_set_operating_mode(ar);
+       }
+       mutex_unlock(&ar->mutex);
+       return -ENOSPC;
+}
+
+static int carl9170_op_sta_add(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta)
+{
+       struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+       unsigned int i;
+
+       if (sta->ht_cap.ht_supported) {
+               if (sta->ht_cap.ampdu_density > 6) {
+                       /*
+                        * HW does support 16us AMPDU density.
+                        * No HT-Xmit for station.
+                        */
+
+                       return 0;
+               }
+
+               for (i = 0; i < CARL9170_NUM_TID; i++)
+                       rcu_assign_pointer(sta_info->agg[i], NULL);
+
+               sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor);
+               sta_info->ht_sta = true;
+       }
+
+       return 0;
+}
+
+static int carl9170_op_sta_remove(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif,
+                               struct ieee80211_sta *sta)
+{
+       struct ar9170 *ar = hw->priv;
+       struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+       unsigned int i;
+       bool cleanup = false;
+
+       if (sta->ht_cap.ht_supported) {
+
+               sta_info->ht_sta = false;
+
+               rcu_read_lock();
+               for (i = 0; i < CARL9170_NUM_TID; i++) {
+                       struct carl9170_sta_tid *tid_info;
+
+                       tid_info = rcu_dereference(sta_info->agg[i]);
+                       rcu_assign_pointer(sta_info->agg[i], NULL);
+
+                       if (!tid_info)
+                               continue;
+
+                       spin_lock_bh(&ar->tx_ampdu_list_lock);
+                       if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN)
+                               tid_info->state = CARL9170_TID_STATE_SHUTDOWN;
+                       spin_unlock_bh(&ar->tx_ampdu_list_lock);
+                       cleanup = true;
+               }
+               rcu_read_unlock();
+
+               if (cleanup)
+                       carl9170_ampdu_gc(ar);
+       }
+
+       return 0;
+}
+
+static int carl9170_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+                              const struct ieee80211_tx_queue_params *param)
+{
+       struct ar9170 *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->mutex);
+       if (queue < ar->hw->queues) {
+               memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param));
+               ret = carl9170_set_qos(ar);
+       } else {
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&ar->mutex);
+       return ret;
+}
+
+static void carl9170_ampdu_work(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        ampdu_work);
+
+       if (!IS_STARTED(ar))
+               return;
+
+       mutex_lock(&ar->mutex);
+       carl9170_ampdu_gc(ar);
+       mutex_unlock(&ar->mutex);
+}
+
+static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   enum ieee80211_ampdu_mlme_action action,
+                                   struct ieee80211_sta *sta,
+                                   u16 tid, u16 *ssn)
+{
+       struct ar9170 *ar = hw->priv;
+       struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+       struct carl9170_sta_tid *tid_info;
+
+       if (modparam_noht)
+               return -EOPNOTSUPP;
+
+       switch (action) {
+       case IEEE80211_AMPDU_TX_START:
+               if (WARN_ON_ONCE(!sta_info->ht_sta))
+                       return -EOPNOTSUPP;
+
+               rcu_read_lock();
+               if (rcu_dereference(sta_info->agg[tid])) {
+                       rcu_read_unlock();
+                       return -EBUSY;
+               }
+
+               tid_info = kzalloc(sizeof(struct carl9170_sta_tid),
+                                  GFP_ATOMIC);
+               if (!tid_info) {
+                       rcu_read_unlock();
+                       return -ENOMEM;
+               }
+
+               tid_info->hsn = tid_info->bsn = tid_info->snx = (*ssn);
+               tid_info->state = CARL9170_TID_STATE_PROGRESS;
+               tid_info->tid = tid;
+               tid_info->max = sta_info->ampdu_max_len;
+
+               INIT_LIST_HEAD(&tid_info->list);
+               INIT_LIST_HEAD(&tid_info->tmp_list);
+               skb_queue_head_init(&tid_info->queue);
+               spin_lock_init(&tid_info->lock);
+
+               spin_lock_bh(&ar->tx_ampdu_list_lock);
+               ar->tx_ampdu_list_len++;
+               list_add_tail_rcu(&tid_info->list, &ar->tx_ampdu_list);
+               rcu_assign_pointer(sta_info->agg[tid], tid_info);
+               spin_unlock_bh(&ar->tx_ampdu_list_lock);
+               rcu_read_unlock();
+
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+
+       case IEEE80211_AMPDU_TX_STOP:
+               rcu_read_lock();
+               tid_info = rcu_dereference(sta_info->agg[tid]);
+               if (tid_info) {
+                       spin_lock_bh(&ar->tx_ampdu_list_lock);
+                       if (tid_info->state > CARL9170_TID_STATE_SHUTDOWN)
+                               tid_info->state = CARL9170_TID_STATE_SHUTDOWN;
+                       spin_unlock_bh(&ar->tx_ampdu_list_lock);
+               }
+
+               rcu_assign_pointer(sta_info->agg[tid], NULL);
+               rcu_read_unlock();
+
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ieee80211_queue_work(ar->hw, &ar->ampdu_work);
+               break;
+
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               rcu_read_lock();
+               tid_info = rcu_dereference(sta_info->agg[tid]);
+
+               sta_info->stats[tid].clear = true;
+
+               if (tid_info) {
+                       bitmap_zero(tid_info->bitmap, CARL9170_BAW_SIZE);
+                       tid_info->state = CARL9170_TID_STATE_IDLE;
+               }
+               rcu_read_unlock();
+
+               if (WARN_ON_ONCE(!tid_info))
+                       return -EFAULT;
+
+               break;
+
+       case IEEE80211_AMPDU_RX_START:
+       case IEEE80211_AMPDU_RX_STOP:
+               /* Handled by hardware */
+               break;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_CARL9170_WPC
+static int carl9170_register_wps_button(struct ar9170 *ar)
+{
+       struct input_dev *input;
+       int err;
+
+       if (!(ar->features & CARL9170_WPS_BUTTON))
+               return 0;
+
+       input = input_allocate_device();
+       if (!input)
+               return -ENOMEM;
+
+       snprintf(ar->wps.name, sizeof(ar->wps.name), "%s WPS Button",
+                wiphy_name(ar->hw->wiphy));
+
+       snprintf(ar->wps.phys, sizeof(ar->wps.phys),
+                "ieee80211/%s/input0", wiphy_name(ar->hw->wiphy));
+
+       input->name = ar->wps.name;
+       input->phys = ar->wps.phys;
+       input->id.bustype = BUS_USB;
+       input->dev.parent = &ar->hw->wiphy->dev;
+
+       input_set_capability(input, EV_KEY, KEY_WPS_BUTTON);
+
+       err = input_register_device(input);
+       if (err) {
+               input_free_device(input);
+               return err;
+       }
+
+       ar->wps.pbc = input;
+       return 0;
+}
+#endif /* CONFIG_CARL9170_WPC */
+
+static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
+                               struct survey_info *survey)
+{
+       struct ar9170 *ar = hw->priv;
+       int err;
+
+       if (idx != 0)
+               return -ENOENT;
+
+       mutex_lock(&ar->mutex);
+       err = carl9170_get_noisefloor(ar);
+       mutex_unlock(&ar->mutex);
+       if (err)
+               return err;
+
+       survey->channel = ar->channel;
+       survey->filled = SURVEY_INFO_NOISE_DBM;
+       survey->noise = ar->noise[0];
+       return 0;
+}
+
+static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop)
+{
+       struct ar9170 *ar = hw->priv;
+       unsigned int vid;
+
+       mutex_lock(&ar->mutex);
+       for_each_set_bit(vid, &ar->vif_bitmap, ar->fw.vif_num)
+               carl9170_flush_cab(ar, vid);
+
+       carl9170_flush(ar, drop);
+       mutex_unlock(&ar->mutex);
+}
+
+static int carl9170_op_get_stats(struct ieee80211_hw *hw,
+                                struct ieee80211_low_level_stats *stats)
+{
+       struct ar9170 *ar = hw->priv;
+
+       memset(stats, 0, sizeof(*stats));
+       stats->dot11ACKFailureCount = ar->tx_ack_failures;
+       stats->dot11FCSErrorCount = ar->tx_fcs_errors;
+       return 0;
+}
+
+static void carl9170_op_sta_notify(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  enum sta_notify_cmd cmd,
+                                  struct ieee80211_sta *sta)
+{
+       struct ar9170 *ar = hw->priv;
+       struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
+       struct sk_buff *skb, *tmp;
+       struct sk_buff_head free;
+       int i;
+
+       switch (cmd) {
+       case STA_NOTIFY_SLEEP:
+               /*
+                * Since the peer is no longer listening, we have to return
+                * as many SKBs as possible back to the mac80211 stack.
+                * It will deal with the retry procedure, once the peer
+                * has become available again.
+                *
+                * NB: Ideally, the driver should return the all frames in
+                * the correct, ascending order. However, I think that this
+                * functionality should be implemented in the stack and not
+                * here...
+                */
+
+               __skb_queue_head_init(&free);
+
+               if (sta->ht_cap.ht_supported) {
+                       rcu_read_lock();
+                       for (i = 0; i < CARL9170_NUM_TID; i++) {
+                               struct carl9170_sta_tid *tid_info;
+
+                               tid_info = rcu_dereference(sta_info->agg[i]);
+
+                               if (!tid_info)
+                                       continue;
+
+                               spin_lock_bh(&ar->tx_ampdu_list_lock);
+                               if (tid_info->state >
+                                   CARL9170_TID_STATE_SUSPEND)
+                                       tid_info->state =
+                                               CARL9170_TID_STATE_SUSPEND;
+                               spin_unlock_bh(&ar->tx_ampdu_list_lock);
+
+                               spin_lock_bh(&tid_info->lock);
+                               while ((skb = __skb_dequeue(&tid_info->queue)))
+                                       __skb_queue_tail(&free, skb);
+                               spin_unlock_bh(&tid_info->lock);
+
+                               ieee80211_stop_tx_ba_session(sta,
+                                       tid_info->tid);
+                       }
+                       rcu_read_unlock();
+               }
+
+               for (i = 0; i < ar->hw->queues; i++) {
+                       spin_lock_bh(&ar->tx_pending[i].lock);
+                       skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) {
+                               struct _carl9170_tx_superframe *super;
+                               struct ieee80211_hdr *hdr;
+
+                               super = (void *) skb->data;
+                               hdr = (void *) super->frame_data;
+
+                               if (compare_ether_addr(hdr->addr1, sta->addr))
+                                       continue;
+
+                               __skb_unlink(skb, &ar->tx_pending[i]);
+                               carl9170_tx_status(ar, skb, false);
+                       }
+                       spin_unlock_bh(&ar->tx_pending[i].lock);
+               }
+
+               while ((skb = __skb_dequeue(&free)))
+                       carl9170_tx_status(ar, skb, false);
+
+               break;
+
+       case STA_NOTIFY_AWAKE:
+               if (!sta->ht_cap.ht_supported)
+                       return;
+
+               rcu_read_lock();
+               for (i = 0; i < CARL9170_NUM_TID; i++) {
+                       struct carl9170_sta_tid *tid_info;
+
+                       tid_info = rcu_dereference(sta_info->agg[i]);
+
+                       if (!tid_info)
+                               continue;
+
+                       if ((tid_info->state == CARL9170_TID_STATE_SUSPEND))
+                               tid_info->state = CARL9170_TID_STATE_IDLE;
+               }
+               rcu_read_unlock();
+               break;
+       }
+}
+
+static const struct ieee80211_ops carl9170_ops = {
+       .start                  = carl9170_op_start,
+       .stop                   = carl9170_op_stop,
+       .tx                     = carl9170_op_tx,
+       .flush                  = carl9170_op_flush,
+       .add_interface          = carl9170_op_add_interface,
+       .remove_interface       = carl9170_op_remove_interface,
+       .config                 = carl9170_op_config,
+       .prepare_multicast      = carl9170_op_prepare_multicast,
+       .configure_filter       = carl9170_op_configure_filter,
+       .conf_tx                = carl9170_op_conf_tx,
+       .bss_info_changed       = carl9170_op_bss_info_changed,
+       .get_tsf                = carl9170_op_get_tsf,
+       .set_key                = carl9170_op_set_key,
+       .sta_add                = carl9170_op_sta_add,
+       .sta_remove             = carl9170_op_sta_remove,
+       .sta_notify             = carl9170_op_sta_notify,
+       .get_survey             = carl9170_op_get_survey,
+       .get_stats              = carl9170_op_get_stats,
+       .ampdu_action           = carl9170_op_ampdu_action,
+};
+
+void *carl9170_alloc(size_t priv_size)
+{
+       struct ieee80211_hw *hw;
+       struct ar9170 *ar;
+       struct sk_buff *skb;
+       int i;
+
+       /*
+        * this buffer is used for rx stream reconstruction.
+        * Under heavy load this device (or the transport layer?)
+        * tends to split the streams into separate rx descriptors.
+        */
+
+       skb = __dev_alloc_skb(AR9170_RX_STREAM_MAX_SIZE, GFP_KERNEL);
+       if (!skb)
+               goto err_nomem;
+
+       hw = ieee80211_alloc_hw(priv_size, &carl9170_ops);
+       if (!hw)
+               goto err_nomem;
+
+       ar = hw->priv;
+       ar->hw = hw;
+       ar->rx_failover = skb;
+
+       memset(&ar->rx_plcp, 0, sizeof(struct ar9170_rx_head));
+       ar->rx_has_plcp = false;
+
+       /*
+        * Here's a hidden pitfall!
+        *
+        * All 4 AC queues work perfectly well under _legacy_ operation.
+        * However as soon as aggregation is enabled, the traffic flow
+        * gets very bumpy. Therefore we have to _switch_ to a
+        * software AC with a single HW queue.
+        */
+       hw->queues = __AR9170_NUM_TXQ;
+
+       mutex_init(&ar->mutex);
+       spin_lock_init(&ar->beacon_lock);
+       spin_lock_init(&ar->cmd_lock);
+       spin_lock_init(&ar->tx_stats_lock);
+       spin_lock_init(&ar->tx_ampdu_list_lock);
+       spin_lock_init(&ar->mem_lock);
+       spin_lock_init(&ar->state_lock);
+       atomic_set(&ar->pending_restarts, 0);
+       ar->vifs = 0;
+       for (i = 0; i < ar->hw->queues; i++) {
+               skb_queue_head_init(&ar->tx_status[i]);
+               skb_queue_head_init(&ar->tx_pending[i]);
+       }
+       INIT_WORK(&ar->ps_work, carl9170_ps_work);
+       INIT_WORK(&ar->restart_work, carl9170_restart_work);
+       INIT_WORK(&ar->ampdu_work, carl9170_ampdu_work);
+       INIT_DELAYED_WORK(&ar->tx_janitor, carl9170_tx_janitor);
+       INIT_LIST_HEAD(&ar->tx_ampdu_list);
+       rcu_assign_pointer(ar->tx_ampdu_iter,
+                          (struct carl9170_sta_tid *) &ar->tx_ampdu_list);
+
+       bitmap_zero(&ar->vif_bitmap, ar->fw.vif_num);
+       INIT_LIST_HEAD(&ar->vif_list);
+       init_completion(&ar->tx_flush);
+
+       /*
+        * Note:
+        * IBSS/ADHOC and AP mode are only enabled, if the firmware
+        * supports these modes. The code which will add the
+        * additional interface_modes is in fw.c.
+        */
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+       hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+                    IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                    IEEE80211_HW_SUPPORTS_PS |
+                    IEEE80211_HW_PS_NULLFUNC_STACK |
+                    IEEE80211_HW_SIGNAL_DBM;
+
+       if (!modparam_noht) {
+               /*
+                * see the comment above, why we allow the user
+                * to disable HT by a module parameter.
+                */
+               hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+       }
+
+       hw->extra_tx_headroom = sizeof(struct _carl9170_tx_superframe);
+       hw->sta_data_size = sizeof(struct carl9170_sta_info);
+       hw->vif_data_size = sizeof(struct carl9170_vif_info);
+
+       hw->max_rates = CARL9170_TX_MAX_RATES;
+       hw->max_rate_tries = CARL9170_TX_USER_RATE_TRIES;
+
+       for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
+               ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
+
+       hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+       return ar;
+
+err_nomem:
+       kfree_skb(skb);
+       return ERR_PTR(-ENOMEM);
+}
+
+static int carl9170_read_eeprom(struct ar9170 *ar)
+{
+#define RW     8       /* number of words to read at once */
+#define RB     (sizeof(u32) * RW)
+       u8 *eeprom = (void *)&ar->eeprom;
+       __le32 offsets[RW];
+       int i, j, err;
+
+       BUILD_BUG_ON(sizeof(ar->eeprom) & 3);
+
+       BUILD_BUG_ON(RB > CARL9170_MAX_CMD_LEN - 4);
+#ifndef __CHECKER__
+       /* don't want to handle trailing remains */
+       BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
+#endif
+
+       for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+               for (j = 0; j < RW; j++)
+                       offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
+                                                RB * i + 4 * j);
+
+               err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
+                                       RB, (u8 *) &offsets,
+                                       RB, eeprom + RB * i);
+               if (err)
+                       return err;
+       }
+
+#undef RW
+#undef RB
+       return 0;
+}
+
+static int carl9170_parse_eeprom(struct ar9170 *ar)
+{
+       struct ath_regulatory *regulatory = &ar->common.regulatory;
+       unsigned int rx_streams, tx_streams, tx_params = 0;
+       int bands = 0;
+
+       if (ar->eeprom.length == cpu_to_le16(0xffff))
+               return -ENODATA;
+
+       rx_streams = hweight8(ar->eeprom.rx_mask);
+       tx_streams = hweight8(ar->eeprom.tx_mask);
+
+       if (rx_streams != tx_streams) {
+               tx_params = IEEE80211_HT_MCS_TX_RX_DIFF;
+
+               WARN_ON(!(tx_streams >= 1 && tx_streams <=
+                       IEEE80211_HT_MCS_TX_MAX_STREAMS));
+
+               tx_params = (tx_streams - 1) <<
+                           IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+
+               carl9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params;
+               carl9170_band_5GHz.ht_cap.mcs.tx_params |= tx_params;
+       }
+
+       if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
+               ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+                       &carl9170_band_2GHz;
+               bands++;
+       }
+       if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
+               ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+                       &carl9170_band_5GHz;
+               bands++;
+       }
+
+       /*
+        * I measured this, a bandswitch takes roughly
+        * 135 ms and a frequency switch about 80.
+        *
+        * FIXME: measure these values again once EEPROM settings
+        *        are used, that will influence them!
+        */
+       if (bands == 2)
+               ar->hw->channel_change_time = 135 * 1000;
+       else
+               ar->hw->channel_change_time = 80 * 1000;
+
+       regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
+       regulatory->current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]);
+
+       /* second part of wiphy init */
+       SET_IEEE80211_PERM_ADDR(ar->hw, ar->eeprom.mac_address);
+
+       return bands ? 0 : -EINVAL;
+}
+
+static int carl9170_reg_notifier(struct wiphy *wiphy,
+                                struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ar9170 *ar = hw->priv;
+
+       return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory);
+}
+
+int carl9170_register(struct ar9170 *ar)
+{
+       struct ath_regulatory *regulatory = &ar->common.regulatory;
+       int err = 0, i;
+
+       if (WARN_ON(ar->mem_bitmap))
+               return -EINVAL;
+
+       ar->mem_bitmap = kzalloc(roundup(ar->fw.mem_blocks, BITS_PER_LONG) *
+                                sizeof(unsigned long), GFP_KERNEL);
+
+       if (!ar->mem_bitmap)
+               return -ENOMEM;
+
+       /* try to read EEPROM, init MAC addr */
+       err = carl9170_read_eeprom(ar);
+       if (err)
+               return err;
+
+       err = carl9170_fw_fix_eeprom(ar);
+       if (err)
+               return err;
+
+       err = carl9170_parse_eeprom(ar);
+       if (err)
+               return err;
+
+       err = ath_regd_init(regulatory, ar->hw->wiphy,
+                           carl9170_reg_notifier);
+       if (err)
+               return err;
+
+       if (modparam_noht) {
+               carl9170_band_2GHz.ht_cap.ht_supported = false;
+               carl9170_band_5GHz.ht_cap.ht_supported = false;
+       }
+
+       for (i = 0; i < ar->fw.vif_num; i++) {
+               ar->vif_priv[i].id = i;
+               ar->vif_priv[i].vif = NULL;
+       }
+
+       err = ieee80211_register_hw(ar->hw);
+       if (err)
+               return err;
+
+       /* mac80211 interface is now registered */
+       ar->registered = true;
+
+       if (!ath_is_world_regd(regulatory))
+               regulatory_hint(ar->hw->wiphy, regulatory->alpha2);
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+       carl9170_debugfs_register(ar);
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+       err = carl9170_led_init(ar);
+       if (err)
+               goto err_unreg;
+
+#ifdef CONFIG_CARL9170_LEDS
+       err = carl9170_led_register(ar);
+       if (err)
+               goto err_unreg;
+#endif /* CONFIG_CAR9L170_LEDS */
+
+#ifdef CONFIG_CARL9170_WPC
+       err = carl9170_register_wps_button(ar);
+       if (err)
+               goto err_unreg;
+#endif /* CONFIG_CARL9170_WPC */
+
+       dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n",
+                wiphy_name(ar->hw->wiphy));
+
+       return 0;
+
+err_unreg:
+       carl9170_unregister(ar);
+       return err;
+}
+
+void carl9170_unregister(struct ar9170 *ar)
+{
+       if (!ar->registered)
+               return;
+
+       ar->registered = false;
+
+#ifdef CONFIG_CARL9170_LEDS
+       carl9170_led_unregister(ar);
+#endif /* CONFIG_CARL9170_LEDS */
+
+#ifdef CONFIG_CARL9170_DEBUGFS
+       carl9170_debugfs_unregister(ar);
+#endif /* CONFIG_CARL9170_DEBUGFS */
+
+#ifdef CONFIG_CARL9170_WPC
+       if (ar->wps.pbc) {
+               input_unregister_device(ar->wps.pbc);
+               ar->wps.pbc = NULL;
+       }
+#endif /* CONFIG_CARL9170_WPC */
+
+       carl9170_cancel_worker(ar);
+       cancel_work_sync(&ar->restart_work);
+
+       ieee80211_unregister_hw(ar->hw);
+}
+
+void carl9170_free(struct ar9170 *ar)
+{
+       WARN_ON(ar->registered);
+       WARN_ON(IS_INITIALIZED(ar));
+
+       kfree_skb(ar->rx_failover);
+       ar->rx_failover = NULL;
+
+       kfree(ar->mem_bitmap);
+       ar->mem_bitmap = NULL;
+
+       mutex_destroy(&ar->mutex);
+
+       ieee80211_free_hw(ar->hw);
+}
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
new file mode 100644 (file)
index 0000000..89deca3
--- /dev/null
@@ -0,0 +1,1810 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * PHY and RF code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/bitrev.h>
+#include "carl9170.h"
+#include "cmd.h"
+#include "phy.h"
+
+static int carl9170_init_power_cal(struct ar9170 *ar)
+{
+       carl9170_regwrite_begin(ar);
+
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE_MAX, 0x7f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE1, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE2, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE3, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE4, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE5, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE6, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE7, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE8, 0x3f3f3f3f);
+       carl9170_regwrite(AR9170_PHY_REG_POWER_TX_RATE9, 0x3f3f3f3f);
+
+       carl9170_regwrite_finish();
+       return carl9170_regwrite_result();
+}
+
+struct carl9170_phy_init {
+       u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20;
+};
+
+static struct carl9170_phy_init ar5416_phy_init[] = {
+       { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, },
+       { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, },
+       { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, },
+       { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, },
+       { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, },
+       { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, },
+       { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+       { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, },
+       { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+       { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+       { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, },
+       { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, },
+       { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, },
+       { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, },
+       { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, },
+       { 0x1c5850, 0x6c48b4e4, 0x6d48b4e4, 0x6d48b0e4, 0x6c48b0e4, },
+       { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, },
+       { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, },
+       { 0x1c585c, 0x31395c5e, 0x3139605e, 0x3139605e, 0x31395c5e, },
+       { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, },
+       { 0x1c5864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+       { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, },
+       { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, },
+       { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, },
+       { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, },
+       { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, },
+       { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, },
+       { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, },
+       { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+       { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, },
+       { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, },
+       { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, },
+       { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, },
+       { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, },
+       { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, },
+       { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, },
+       { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, },
+       { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, },
+       { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, },
+       { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, },
+       { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, },
+       { 0x1c59bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+       { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, },
+       { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, },
+       { 0x1c59c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, },
+       { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, },
+       { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, },
+       { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, },
+       { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, },
+       { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, },
+       { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, },
+       { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, },
+       { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, },
+       { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, },
+       { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, },
+       { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, },
+       { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, },
+       { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, },
+       { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, },
+       { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, },
+       { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, },
+       { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+       { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, },
+       { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, },
+       { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, },
+       { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, },
+       { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, },
+       { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, },
+       { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, },
+       { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, },
+       { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, },
+       { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, },
+       { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, },
+       { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, },
+       { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, },
+       { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, },
+       { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, },
+       { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, },
+       { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, },
+       { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, },
+       { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, },
+       { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, },
+       { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, },
+       { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, },
+       { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, },
+       { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, },
+       { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, },
+       { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, },
+       { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, },
+       { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, },
+       { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, },
+       { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, },
+       { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, },
+       { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+       { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, },
+       { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, },
+       { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, },
+       { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, },
+       { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, },
+       { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, },
+       { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, },
+       { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+       { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, },
+       { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, },
+       { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, },
+       { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, },
+       { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, },
+       { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, },
+       { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, },
+       { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+       { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, },
+       { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, },
+       { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, },
+       { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, },
+       { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, },
+       { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, },
+       { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, },
+       { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, },
+       { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, },
+       { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+       { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, },
+       { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, },
+       { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, },
+       { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, },
+       { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, },
+       { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, },
+       { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, },
+       { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, },
+       { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, },
+       { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, },
+       { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+       { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+       { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, },
+       { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, },
+       { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, },
+       { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, },
+       { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, },
+       { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, },
+       { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, },
+       { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, },
+       { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, },
+       { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, },
+       { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, },
+       { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, },
+       { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, },
+       { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, },
+       { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, },
+       { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+       { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, },
+       { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, },
+       { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, },
+       { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, },
+       { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+       { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, },
+       { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, },
+       { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, },
+       { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, },
+       { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, },
+       { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, },
+       { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, },
+       { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, },
+       { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, },
+       { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, },
+       { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, },
+       { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, },
+       { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, },
+       { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, },
+       { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, },
+       { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, },
+       { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, },
+       { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, },
+       { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+       { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+       { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+       { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+       { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+       { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+/*     { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */
+       { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+       { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, },
+       { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, },
+       { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+       { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, },
+       { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, },
+       { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, },
+       { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, },
+       { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, },
+       { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, },
+       { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, },
+       { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, },
+       { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, },
+       { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, },
+       { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, },
+       { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, }
+};
+
+/*
+ * look up a certain register in ar5416_phy_init[] and return the init. value
+ * for the band and bandwidth given. Return 0 if register address not found.
+ */
+static u32 carl9170_def_val(u32 reg, bool is_2ghz, bool is_40mhz)
+{
+       unsigned int i;
+       for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+               if (ar5416_phy_init[i].reg != reg)
+                       continue;
+
+               if (is_2ghz) {
+                       if (is_40mhz)
+                               return ar5416_phy_init[i]._2ghz_40;
+                       else
+                               return ar5416_phy_init[i]._2ghz_20;
+               } else {
+                       if (is_40mhz)
+                               return ar5416_phy_init[i]._5ghz_40;
+                       else
+                               return ar5416_phy_init[i]._5ghz_20;
+               }
+       }
+       return 0;
+}
+
+/*
+ * initialize some phy regs from eeprom values in modal_header[]
+ * acc. to band and bandwith
+ */
+static int carl9170_init_phy_from_eeprom(struct ar9170 *ar,
+                               bool is_2ghz, bool is_40mhz)
+{
+       static const u8 xpd2pd[16] = {
+               0x2, 0x2, 0x2, 0x1, 0x2, 0x2, 0x6, 0x2,
+               0x2, 0x3, 0x7, 0x2, 0xb, 0x2, 0x2, 0x2
+       };
+       /* pointer to the modal_header acc. to band */
+       struct ar9170_eeprom_modal *m = &ar->eeprom.modal_header[is_2ghz];
+       u32 val;
+
+       carl9170_regwrite_begin(ar);
+
+       /* ant common control (index 0) */
+       carl9170_regwrite(AR9170_PHY_REG_SWITCH_COM,
+               le32_to_cpu(m->antCtrlCommon));
+
+       /* ant control chain 0 (index 1) */
+       carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_0,
+               le32_to_cpu(m->antCtrlChain[0]));
+
+       /* ant control chain 2 (index 2) */
+       carl9170_regwrite(AR9170_PHY_REG_SWITCH_CHAIN_2,
+               le32_to_cpu(m->antCtrlChain[1]));
+
+       /* SwSettle (index 3) */
+       if (!is_40mhz) {
+               val = carl9170_def_val(AR9170_PHY_REG_SETTLING,
+                                    is_2ghz, is_40mhz);
+               SET_VAL(AR9170_PHY_SETTLING_SWITCH, val, m->switchSettling);
+               carl9170_regwrite(AR9170_PHY_REG_SETTLING, val);
+       }
+
+       /* adcDesired, pdaDesired (index 4) */
+       val = carl9170_def_val(AR9170_PHY_REG_DESIRED_SZ, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_DESIRED_SZ_PGA, val, m->pgaDesiredSize);
+       SET_VAL(AR9170_PHY_DESIRED_SZ_ADC, val, m->adcDesiredSize);
+       carl9170_regwrite(AR9170_PHY_REG_DESIRED_SZ, val);
+
+       /* TxEndToXpaOff, TxFrameToXpaOn (index 5) */
+       val = carl9170_def_val(AR9170_PHY_REG_RF_CTL4, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF, val, m->txEndToXpaOff);
+       SET_VAL(AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF, val, m->txEndToXpaOff);
+       SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAB_ON, val, m->txFrameToXpaOn);
+       SET_VAL(AR9170_PHY_RF_CTL4_FRAME_XPAA_ON, val, m->txFrameToXpaOn);
+       carl9170_regwrite(AR9170_PHY_REG_RF_CTL4, val);
+
+       /* TxEndToRxOn (index 6) */
+       val = carl9170_def_val(AR9170_PHY_REG_RF_CTL3, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON, val, m->txEndToRxOn);
+       carl9170_regwrite(AR9170_PHY_REG_RF_CTL3, val);
+
+       /* thresh62 (index 7) */
+       val = carl9170_def_val(0x1c8864, is_2ghz, is_40mhz);
+       val = (val & ~0x7f000) | (m->thresh62 << 12);
+       carl9170_regwrite(0x1c8864, val);
+
+       /* tx/rx attenuation chain 0 (index 8) */
+       val = carl9170_def_val(AR9170_PHY_REG_RXGAIN, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[0]);
+       carl9170_regwrite(AR9170_PHY_REG_RXGAIN, val);
+
+       /* tx/rx attenuation chain 2 (index 9) */
+       val = carl9170_def_val(AR9170_PHY_REG_RXGAIN_CHAIN_2,
+                              is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_RXGAIN_TXRX_ATTEN, val, m->txRxAttenCh[1]);
+       carl9170_regwrite(AR9170_PHY_REG_RXGAIN_CHAIN_2, val);
+
+       /* tx/rx margin chain 0 (index 10) */
+       val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[0]);
+       /* bsw margin chain 0 for 5GHz only */
+       if (!is_2ghz)
+               SET_VAL(AR9170_PHY_GAIN_2GHZ_BSW_MARGIN, val, m->bswMargin[0]);
+       carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ, val);
+
+       /* tx/rx margin chain 2 (index 11) */
+       val = carl9170_def_val(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2,
+                              is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN, val, m->rxTxMarginCh[1]);
+       carl9170_regwrite(AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2, val);
+
+       /* iqCall, iqCallq chain 0 (index 12) */
+       val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(0),
+                              is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[0]);
+       SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[0]);
+       carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(0), val);
+
+       /* iqCall, iqCallq chain 2 (index 13) */
+       val = carl9170_def_val(AR9170_PHY_REG_TIMING_CTRL4(2),
+                              is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, val, m->iqCalICh[1]);
+       SET_VAL(AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, val, m->iqCalQCh[1]);
+       carl9170_regwrite(AR9170_PHY_REG_TIMING_CTRL4(2), val);
+
+       /* xpd gain mask (index 14) */
+       val = carl9170_def_val(AR9170_PHY_REG_TPCRG1, is_2ghz, is_40mhz);
+       SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_1, val,
+               xpd2pd[m->xpdGain & 0xf] & 3);
+       SET_VAL(AR9170_PHY_TPCRG1_PD_GAIN_2, val,
+               xpd2pd[m->xpdGain & 0xf] >> 2);
+       carl9170_regwrite(AR9170_PHY_REG_TPCRG1, val);
+
+       carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask);
+       carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask);
+
+       carl9170_regwrite_finish();
+       return carl9170_regwrite_result();
+}
+
+static int carl9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
+{
+       int i, err;
+       u32 val;
+       bool is_2ghz = band == IEEE80211_BAND_2GHZ;
+       bool is_40mhz = conf_is_ht40(&ar->hw->conf);
+
+       carl9170_regwrite_begin(ar);
+
+       for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+               if (is_40mhz) {
+                       if (is_2ghz)
+                               val = ar5416_phy_init[i]._2ghz_40;
+                       else
+                               val = ar5416_phy_init[i]._5ghz_40;
+               } else {
+                       if (is_2ghz)
+                               val = ar5416_phy_init[i]._2ghz_20;
+                       else
+                               val = ar5416_phy_init[i]._5ghz_20;
+               }
+
+               carl9170_regwrite(ar5416_phy_init[i].reg, val);
+       }
+
+       carl9170_regwrite_finish();
+       err = carl9170_regwrite_result();
+       if (err)
+               return err;
+
+       err = carl9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz);
+       if (err)
+               return err;
+
+       err = carl9170_init_power_cal(ar);
+       if (err)
+               return err;
+
+       /* XXX: remove magic! */
+       if (is_2ghz)
+               err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5163);
+       else
+               err = carl9170_write_reg(ar, AR9170_PWR_REG_PLL_ADDAC, 0x5143);
+
+       return err;
+}
+
+struct carl9170_rf_initvals {
+       u32 reg, _5ghz, _2ghz;
+};
+
+static struct carl9170_rf_initvals carl9170_rf_initval[] = {
+       /* bank 0 */
+       { 0x1c58b0, 0x1e5795e5, 0x1e5795e5},
+       { 0x1c58e0, 0x02008020, 0x02008020},
+       /* bank 1 */
+       { 0x1c58b0, 0x02108421, 0x02108421},
+       { 0x1c58ec, 0x00000008, 0x00000008},
+       /* bank 2 */
+       { 0x1c58b0, 0x0e73ff17, 0x0e73ff17},
+       { 0x1c58e0, 0x00000420, 0x00000420},
+       /* bank 3 */
+       { 0x1c58f0, 0x01400018, 0x01c00018},
+       /* bank 4 */
+       { 0x1c58b0, 0x000001a1, 0x000001a1},
+       { 0x1c58e8, 0x00000001, 0x00000001},
+       /* bank 5 */
+       { 0x1c58b0, 0x00000013, 0x00000013},
+       { 0x1c58e4, 0x00000002, 0x00000002},
+       /* bank 6 */
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00004000, 0x00004000},
+       { 0x1c58b0, 0x00006c00, 0x00006c00},
+       { 0x1c58b0, 0x00002c00, 0x00002c00},
+       { 0x1c58b0, 0x00004800, 0x00004800},
+       { 0x1c58b0, 0x00004000, 0x00004000},
+       { 0x1c58b0, 0x00006000, 0x00006000},
+       { 0x1c58b0, 0x00001000, 0x00001000},
+       { 0x1c58b0, 0x00004000, 0x00004000},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00087c00, 0x00087c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00005400, 0x00005400},
+       { 0x1c58b0, 0x00000c00, 0x00000c00},
+       { 0x1c58b0, 0x00001800, 0x00001800},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00006c00, 0x00006c00},
+       { 0x1c58b0, 0x00006c00, 0x00006c00},
+       { 0x1c58b0, 0x00007c00, 0x00007c00},
+       { 0x1c58b0, 0x00002c00, 0x00002c00},
+       { 0x1c58b0, 0x00003c00, 0x00003c00},
+       { 0x1c58b0, 0x00003800, 0x00003800},
+       { 0x1c58b0, 0x00001c00, 0x00001c00},
+       { 0x1c58b0, 0x00000800, 0x00000800},
+       { 0x1c58b0, 0x00000408, 0x00000408},
+       { 0x1c58b0, 0x00004c15, 0x00004c15},
+       { 0x1c58b0, 0x00004188, 0x00004188},
+       { 0x1c58b0, 0x0000201e, 0x0000201e},
+       { 0x1c58b0, 0x00010408, 0x00010408},
+       { 0x1c58b0, 0x00000801, 0x00000801},
+       { 0x1c58b0, 0x00000c08, 0x00000c08},
+       { 0x1c58b0, 0x0000181e, 0x0000181e},
+       { 0x1c58b0, 0x00001016, 0x00001016},
+       { 0x1c58b0, 0x00002800, 0x00002800},
+       { 0x1c58b0, 0x00004010, 0x00004010},
+       { 0x1c58b0, 0x0000081c, 0x0000081c},
+       { 0x1c58b0, 0x00000115, 0x00000115},
+       { 0x1c58b0, 0x00000015, 0x00000015},
+       { 0x1c58b0, 0x00000066, 0x00000066},
+       { 0x1c58b0, 0x0000001c, 0x0000001c},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000004, 0x00000004},
+       { 0x1c58b0, 0x00000015, 0x00000015},
+       { 0x1c58b0, 0x0000001f, 0x0000001f},
+       { 0x1c58e0, 0x00000000, 0x00000400},
+       /* bank 7 */
+       { 0x1c58b0, 0x000000a0, 0x000000a0},
+       { 0x1c58b0, 0x00000000, 0x00000000},
+       { 0x1c58b0, 0x00000040, 0x00000040},
+       { 0x1c58f0, 0x0000001c, 0x0000001c},
+};
+
+static int carl9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz)
+{
+       int err, i;
+
+       carl9170_regwrite_begin(ar);
+
+       for (i = 0; i < ARRAY_SIZE(carl9170_rf_initval); i++)
+               carl9170_regwrite(carl9170_rf_initval[i].reg,
+                                 band5ghz ? carl9170_rf_initval[i]._5ghz
+                                          : carl9170_rf_initval[i]._2ghz);
+
+       carl9170_regwrite_finish();
+       err = carl9170_regwrite_result();
+       if (err)
+               wiphy_err(ar->hw->wiphy, "rf init failed\n");
+
+       return err;
+}
+
+struct carl9170_phy_freq_params {
+       u8 coeff_exp;
+       u16 coeff_man;
+       u8 coeff_exp_shgi;
+       u16 coeff_man_shgi;
+};
+
+enum carl9170_bw {
+       CARL9170_BW_20,
+       CARL9170_BW_40_BELOW,
+       CARL9170_BW_40_ABOVE,
+
+       __CARL9170_NUM_BW,
+};
+
+struct carl9170_phy_freq_entry {
+       u16 freq;
+       struct carl9170_phy_freq_params params[__CARL9170_NUM_BW];
+};
+
+/* NB: must be in sync with channel tables in main! */
+static const struct carl9170_phy_freq_entry carl9170_phy_freq_params[] = {
+/*
+ *     freq,
+ *             20MHz,
+ *             40MHz (below),
+ *             40Mhz (above),
+ */
+       { 2412, {
+               { 3, 21737, 3, 19563, },
+               { 3, 21827, 3, 19644, },
+               { 3, 21647, 3, 19482, },
+       } },
+       { 2417, {
+               { 3, 21692, 3, 19523, },
+               { 3, 21782, 3, 19604, },
+               { 3, 21602, 3, 19442, },
+       } },
+       { 2422, {
+               { 3, 21647, 3, 19482, },
+               { 3, 21737, 3, 19563, },
+               { 3, 21558, 3, 19402, },
+       } },
+       { 2427, {
+               { 3, 21602, 3, 19442, },
+               { 3, 21692, 3, 19523, },
+               { 3, 21514, 3, 19362, },
+       } },
+       { 2432, {
+               { 3, 21558, 3, 19402, },
+               { 3, 21647, 3, 19482, },
+               { 3, 21470, 3, 19323, },
+       } },
+       { 2437, {
+               { 3, 21514, 3, 19362, },
+               { 3, 21602, 3, 19442, },
+               { 3, 21426, 3, 19283, },
+       } },
+       { 2442, {
+               { 3, 21470, 3, 19323, },
+               { 3, 21558, 3, 19402, },
+               { 3, 21382, 3, 19244, },
+       } },
+       { 2447, {
+               { 3, 21426, 3, 19283, },
+               { 3, 21514, 3, 19362, },
+               { 3, 21339, 3, 19205, },
+       } },
+       { 2452, {
+               { 3, 21382, 3, 19244, },
+               { 3, 21470, 3, 19323, },
+               { 3, 21295, 3, 19166, },
+       } },
+       { 2457, {
+               { 3, 21339, 3, 19205, },
+               { 3, 21426, 3, 19283, },
+               { 3, 21252, 3, 19127, },
+       } },
+       { 2462, {
+               { 3, 21295, 3, 19166, },
+               { 3, 21382, 3, 19244, },
+               { 3, 21209, 3, 19088, },
+       } },
+       { 2467, {
+               { 3, 21252, 3, 19127, },
+               { 3, 21339, 3, 19205, },
+               { 3, 21166, 3, 19050, },
+       } },
+       { 2472, {
+               { 3, 21209, 3, 19088, },
+               { 3, 21295, 3, 19166, },
+               { 3, 21124, 3, 19011, },
+       } },
+       { 2484, {
+               { 3, 21107, 3, 18996, },
+               { 3, 21192, 3, 19073, },
+               { 3, 21022, 3, 18920, },
+       } },
+       { 4920, {
+               { 4, 21313, 4, 19181, },
+               { 4, 21356, 4, 19220, },
+               { 4, 21269, 4, 19142, },
+       } },
+       { 4940, {
+               { 4, 21226, 4, 19104, },
+               { 4, 21269, 4, 19142, },
+               { 4, 21183, 4, 19065, },
+       } },
+       { 4960, {
+               { 4, 21141, 4, 19027, },
+               { 4, 21183, 4, 19065, },
+               { 4, 21098, 4, 18988, },
+       } },
+       { 4980, {
+               { 4, 21056, 4, 18950, },
+               { 4, 21098, 4, 18988, },
+               { 4, 21014, 4, 18912, },
+       } },
+       { 5040, {
+               { 4, 20805, 4, 18725, },
+               { 4, 20846, 4, 18762, },
+               { 4, 20764, 4, 18687, },
+       } },
+       { 5060, {
+               { 4, 20723, 4, 18651, },
+               { 4, 20764, 4, 18687, },
+               { 4, 20682, 4, 18614, },
+       } },
+       { 5080, {
+               { 4, 20641, 4, 18577, },
+               { 4, 20682, 4, 18614, },
+               { 4, 20601, 4, 18541, },
+       } },
+       { 5180, {
+               { 4, 20243, 4, 18219, },
+               { 4, 20282, 4, 18254, },
+               { 4, 20204, 4, 18183, },
+       } },
+       { 5200, {
+               { 4, 20165, 4, 18148, },
+               { 4, 20204, 4, 18183, },
+               { 4, 20126, 4, 18114, },
+       } },
+       { 5220, {
+               { 4, 20088, 4, 18079, },
+               { 4, 20126, 4, 18114, },
+               { 4, 20049, 4, 18044, },
+       } },
+       { 5240, {
+               { 4, 20011, 4, 18010, },
+               { 4, 20049, 4, 18044, },
+               { 4, 19973, 4, 17976, },
+       } },
+       { 5260, {
+               { 4, 19935, 4, 17941, },
+               { 4, 19973, 4, 17976, },
+               { 4, 19897, 4, 17907, },
+       } },
+       { 5280, {
+               { 4, 19859, 4, 17873, },
+               { 4, 19897, 4, 17907, },
+               { 4, 19822, 4, 17840, },
+       } },
+       { 5300, {
+               { 4, 19784, 4, 17806, },
+               { 4, 19822, 4, 17840, },
+               { 4, 19747, 4, 17772, },
+       } },
+       { 5320, {
+               { 4, 19710, 4, 17739, },
+               { 4, 19747, 4, 17772, },
+               { 4, 19673, 4, 17706, },
+       } },
+       { 5500, {
+               { 4, 19065, 4, 17159, },
+               { 4, 19100, 4, 17190, },
+               { 4, 19030, 4, 17127, },
+       } },
+       { 5520, {
+               { 4, 18996, 4, 17096, },
+               { 4, 19030, 4, 17127, },
+               { 4, 18962, 4, 17065, },
+       } },
+       { 5540, {
+               { 4, 18927, 4, 17035, },
+               { 4, 18962, 4, 17065, },
+               { 4, 18893, 4, 17004, },
+       } },
+       { 5560, {
+               { 4, 18859, 4, 16973, },
+               { 4, 18893, 4, 17004, },
+               { 4, 18825, 4, 16943, },
+       } },
+       { 5580, {
+               { 4, 18792, 4, 16913, },
+               { 4, 18825, 4, 16943, },
+               { 4, 18758, 4, 16882, },
+       } },
+       { 5600, {
+               { 4, 18725, 4, 16852, },
+               { 4, 18758, 4, 16882, },
+               { 4, 18691, 4, 16822, },
+       } },
+       { 5620, {
+               { 4, 18658, 4, 16792, },
+               { 4, 18691, 4, 16822, },
+               { 4, 18625, 4, 16762, },
+       } },
+       { 5640, {
+               { 4, 18592, 4, 16733, },
+               { 4, 18625, 4, 16762, },
+               { 4, 18559, 4, 16703, },
+       } },
+       { 5660, {
+               { 4, 18526, 4, 16673, },
+               { 4, 18559, 4, 16703, },
+               { 4, 18493, 4, 16644, },
+       } },
+       { 5680, {
+               { 4, 18461, 4, 16615, },
+               { 4, 18493, 4, 16644, },
+               { 4, 18428, 4, 16586, },
+       } },
+       { 5700, {
+               { 4, 18396, 4, 16556, },
+               { 4, 18428, 4, 16586, },
+               { 4, 18364, 4, 16527, },
+       } },
+       { 5745, {
+               { 4, 18252, 4, 16427, },
+               { 4, 18284, 4, 16455, },
+               { 4, 18220, 4, 16398, },
+       } },
+       { 5765, {
+               { 4, 18189, 5, 32740, },
+               { 4, 18220, 4, 16398, },
+               { 4, 18157, 5, 32683, },
+       } },
+       { 5785, {
+               { 4, 18126, 5, 32626, },
+               { 4, 18157, 5, 32683, },
+               { 4, 18094, 5, 32570, },
+       } },
+       { 5805, {
+               { 4, 18063, 5, 32514, },
+               { 4, 18094, 5, 32570, },
+               { 4, 18032, 5, 32458, },
+       } },
+       { 5825, {
+               { 4, 18001, 5, 32402, },
+               { 4, 18032, 5, 32458, },
+               { 4, 17970, 5, 32347, },
+       } },
+       { 5170, {
+               { 4, 20282, 4, 18254, },
+               { 4, 20321, 4, 18289, },
+               { 4, 20243, 4, 18219, },
+       } },
+       { 5190, {
+               { 4, 20204, 4, 18183, },
+               { 4, 20243, 4, 18219, },
+               { 4, 20165, 4, 18148, },
+       } },
+       { 5210, {
+               { 4, 20126, 4, 18114, },
+               { 4, 20165, 4, 18148, },
+               { 4, 20088, 4, 18079, },
+       } },
+       { 5230, {
+               { 4, 20049, 4, 18044, },
+               { 4, 20088, 4, 18079, },
+               { 4, 20011, 4, 18010, },
+       } },
+};
+
+static int carl9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
+                                     u32 freq, enum carl9170_bw bw)
+{
+       int err;
+       u32 d0, d1, td0, td1, fd0, fd1;
+       u8 chansel;
+       u8 refsel0 = 1, refsel1 = 0;
+       u8 lf_synth = 0;
+
+       switch (bw) {
+       case CARL9170_BW_40_ABOVE:
+               freq += 10;
+               break;
+       case CARL9170_BW_40_BELOW:
+               freq -= 10;
+               break;
+       case CARL9170_BW_20:
+               break;
+       default:
+               BUG();
+               return -ENOSYS;
+       }
+
+       if (band5ghz) {
+               if (freq % 10) {
+                       chansel = (freq - 4800) / 5;
+               } else {
+                       chansel = ((freq - 4800) / 10) * 2;
+                       refsel0 = 0;
+                       refsel1 = 1;
+               }
+               chansel = byte_rev_table[chansel];
+       } else {
+               if (freq == 2484) {
+                       chansel = 10 + (freq - 2274) / 5;
+                       lf_synth = 1;
+               } else
+                       chansel = 16 + (freq - 2272) / 5;
+               chansel *= 4;
+               chansel = byte_rev_table[chansel];
+       }
+
+       d1 =    chansel;
+       d0 =    0x21 |
+               refsel0 << 3 |
+               refsel1 << 2 |
+               lf_synth << 1;
+       td0 =   d0 & 0x1f;
+       td1 =   d1 & 0x1f;
+       fd0 =   td1 << 5 | td0;
+
+       td0 =   (d0 >> 5) & 0x7;
+       td1 =   (d1 >> 5) & 0x7;
+       fd1 =   td1 << 5 | td0;
+
+       carl9170_regwrite_begin(ar);
+
+       carl9170_regwrite(0x1c58b0, fd0);
+       carl9170_regwrite(0x1c58e8, fd1);
+
+       carl9170_regwrite_finish();
+       err = carl9170_regwrite_result();
+       if (err)
+               return err;
+
+       msleep(20);
+
+       return 0;
+}
+
+static const struct carl9170_phy_freq_params *
+carl9170_get_hw_dyn_params(struct ieee80211_channel *channel,
+                          enum carl9170_bw bw)
+{
+       unsigned int chanidx = 0;
+       u16 freq = 2412;
+
+       if (channel) {
+               chanidx = channel->hw_value;
+               freq = channel->center_freq;
+       }
+
+       BUG_ON(chanidx >= ARRAY_SIZE(carl9170_phy_freq_params));
+
+       BUILD_BUG_ON(__CARL9170_NUM_BW != 3);
+
+       WARN_ON(carl9170_phy_freq_params[chanidx].freq != freq);
+
+       return &carl9170_phy_freq_params[chanidx].params[bw];
+}
+
+static int carl9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f)
+{
+       int idx = nfreqs - 2;
+
+       while (idx >= 0) {
+               if (f >= freqs[idx])
+                       return idx;
+               idx--;
+       }
+
+       return 0;
+}
+
+static s32 carl9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+       /* nothing to interpolate, it's horizontal */
+       if (y2 == y1)
+               return y1;
+
+       /* check if we hit one of the edges */
+       if (x == x1)
+               return y1;
+       if (x == x2)
+               return y2;
+
+       /* x1 == x2 is bad, hopefully == x */
+       if (x2 == x1)
+               return y1;
+
+       return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1));
+}
+
+static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
+{
+#define SHIFT          8
+       s32 y;
+
+       y = carl9170_interpolate_s32(x << SHIFT, x1 << SHIFT,
+               y1 << SHIFT, x2 << SHIFT, y2 << SHIFT);
+
+       /*
+        * XXX: unwrap this expression
+        *      Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)?
+        *      Can we rely on the compiler to optimise away the div?
+        */
+       return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+#undef SHIFT
+}
+
+static u8 carl9170_interpolate_val(u8 x, u8 *x_array, u8 *y_array)
+{
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               if (x <= x_array[i + 1])
+                       break;
+       }
+
+       return carl9170_interpolate_u8(x, x_array[i], y_array[i],
+               x_array[i + 1], y_array[i + 1]);
+}
+
+static int carl9170_set_freq_cal_data(struct ar9170 *ar,
+       struct ieee80211_channel *channel)
+{
+       u8 *cal_freq_pier;
+       u8 vpds[2][AR5416_PD_GAIN_ICEPTS];
+       u8 pwrs[2][AR5416_PD_GAIN_ICEPTS];
+       int chain, idx, i;
+       u32 phy_data = 0;
+       u8 f, tmp;
+
+       switch (channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               f = channel->center_freq - 2300;
+               cal_freq_pier = ar->eeprom.cal_freq_pier_2G;
+               i = AR5416_NUM_2G_CAL_PIERS - 1;
+               break;
+
+       case IEEE80211_BAND_5GHZ:
+               f = (channel->center_freq - 4800) / 5;
+               cal_freq_pier = ar->eeprom.cal_freq_pier_5G;
+               i = AR5416_NUM_5G_CAL_PIERS - 1;
+               break;
+
+       default:
+               return -EINVAL;
+               break;
+       }
+
+       for (; i >= 0; i--) {
+               if (cal_freq_pier[i] != 0xff)
+                       break;
+       }
+       if (i < 0)
+               return -EINVAL;
+
+       idx = carl9170_find_freq_idx(i, cal_freq_pier, f);
+
+       carl9170_regwrite_begin(ar);
+
+       for (chain = 0; chain < AR5416_MAX_CHAINS; chain++) {
+               for (i = 0; i < AR5416_PD_GAIN_ICEPTS; i++) {
+                       struct ar9170_calibration_data_per_freq *cal_pier_data;
+                       int j;
+
+                       switch (channel->band) {
+                       case IEEE80211_BAND_2GHZ:
+                               cal_pier_data = &ar->eeprom.
+                                       cal_pier_data_2G[chain][idx];
+                               break;
+
+                       case IEEE80211_BAND_5GHZ:
+                               cal_pier_data = &ar->eeprom.
+                                       cal_pier_data_5G[chain][idx];
+                               break;
+
+                       default:
+                               return -EINVAL;
+                       }
+
+                       for (j = 0; j < 2; j++) {
+                               vpds[j][i] = carl9170_interpolate_u8(f,
+                                       cal_freq_pier[idx],
+                                       cal_pier_data->vpd_pdg[j][i],
+                                       cal_freq_pier[idx + 1],
+                                       cal_pier_data[1].vpd_pdg[j][i]);
+
+                               pwrs[j][i] = carl9170_interpolate_u8(f,
+                                       cal_freq_pier[idx],
+                                       cal_pier_data->pwr_pdg[j][i],
+                                       cal_freq_pier[idx + 1],
+                                       cal_pier_data[1].pwr_pdg[j][i]) / 2;
+                       }
+               }
+
+               for (i = 0; i < 76; i++) {
+                       if (i < 25) {
+                               tmp = carl9170_interpolate_val(i, &pwrs[0][0],
+                                                              &vpds[0][0]);
+                       } else {
+                               tmp = carl9170_interpolate_val(i - 12,
+                                                              &pwrs[1][0],
+                                                              &vpds[1][0]);
+                       }
+
+                       phy_data |= tmp << ((i & 3) << 3);
+                       if ((i & 3) == 3) {
+                               carl9170_regwrite(0x1c6280 + chain * 0x1000 +
+                                                 (i & ~3), phy_data);
+                               phy_data = 0;
+                       }
+               }
+
+               for (i = 19; i < 32; i++)
+                       carl9170_regwrite(0x1c6280 + chain * 0x1000 + (i << 2),
+                                         0x0);
+       }
+
+       carl9170_regwrite_finish();
+       return carl9170_regwrite_result();
+}
+
+static u8 carl9170_get_max_edge_power(struct ar9170 *ar,
+       u32 freq, struct ar9170_calctl_edges edges[])
+{
+       int i;
+       u8 rc = AR5416_MAX_RATE_POWER;
+       u8 f;
+       if (freq < 3000)
+               f = freq - 2300;
+       else
+               f = (freq - 4800) / 5;
+
+       for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
+               if (edges[i].channel == 0xff)
+                       break;
+               if (f == edges[i].channel) {
+                       /* exact freq match */
+                       rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS;
+                       break;
+               }
+               if (i > 0 && f < edges[i].channel) {
+                       if (f > edges[i - 1].channel &&
+                           edges[i - 1].power_flags &
+                           AR9170_CALCTL_EDGE_FLAGS) {
+                               /* lower channel has the inband flag set */
+                               rc = edges[i - 1].power_flags &
+                                       ~AR9170_CALCTL_EDGE_FLAGS;
+                       }
+                       break;
+               }
+       }
+
+       if (i == AR5416_NUM_BAND_EDGES) {
+               if (f > edges[i - 1].channel &&
+                   edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
+                       /* lower channel has the inband flag set */
+                       rc = edges[i - 1].power_flags &
+                               ~AR9170_CALCTL_EDGE_FLAGS;
+               }
+       }
+       return rc;
+}
+
+static u8 carl9170_get_heavy_clip(struct ar9170 *ar, u32 freq,
+       enum carl9170_bw bw, struct ar9170_calctl_edges edges[])
+{
+       u8 f;
+       int i;
+       u8 rc = 0;
+
+       if (freq < 3000)
+               f = freq - 2300;
+       else
+               f = (freq - 4800) / 5;
+
+       if (bw == CARL9170_BW_40_BELOW || bw == CARL9170_BW_40_ABOVE)
+               rc |= 0xf0;
+
+       for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
+               if (edges[i].channel == 0xff)
+                       break;
+               if (f == edges[i].channel) {
+                       if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS))
+                               rc |= 0x0f;
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+/*
+ * calculate the conformance test limits and the heavy clip parameter
+ * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706)
+ */
+static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw)
+{
+       u8 ctl_grp; /* CTL group */
+       u8 ctl_idx; /* CTL index */
+       int i, j;
+       struct ctl_modes {
+               u8 ctl_mode;
+               u8 max_power;
+               u8 *pwr_cal_data;
+               int pwr_cal_len;
+       } *modes;
+
+       /*
+        * order is relevant in the mode_list_*: we fall back to the
+        * lower indices if any mode is missed in the EEPROM.
+        */
+       struct ctl_modes mode_list_2ghz[] = {
+               { CTL_11B, 0, ar->power_2G_cck, 4 },
+               { CTL_11G, 0, ar->power_2G_ofdm, 4 },
+               { CTL_2GHT20, 0, ar->power_2G_ht20, 8 },
+               { CTL_2GHT40, 0, ar->power_2G_ht40, 8 },
+       };
+       struct ctl_modes mode_list_5ghz[] = {
+               { CTL_11A, 0, ar->power_5G_leg, 4 },
+               { CTL_5GHT20, 0, ar->power_5G_ht20, 8 },
+               { CTL_5GHT40, 0, ar->power_5G_ht40, 8 },
+       };
+       int nr_modes;
+
+#define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n])
+
+       ar->heavy_clip = 0;
+
+       /*
+        * TODO: investigate the differences between OTUS'
+        * hpreg.c::zfHpGetRegulatoryDomain() and
+        * ath/regd.c::ath_regd_get_band_ctl() -
+        * e.g. for FCC3_WORLD the OTUS procedure
+        * always returns CTL_FCC, while the one in ath/ delivers
+        * CTL_ETSI for 2GHz and CTL_FCC for 5GHz.
+        */
+       ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory,
+                                       ar->hw->conf.channel->band);
+
+       /* ctl group not found - either invalid band (NO_CTL) or ww roaming */
+       if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL)
+               ctl_grp = CTL_FCC;
+
+       if (ctl_grp != CTL_FCC)
+               /* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */
+               return;
+
+       if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) {
+               modes = mode_list_2ghz;
+               nr_modes = ARRAY_SIZE(mode_list_2ghz);
+       } else {
+               modes = mode_list_5ghz;
+               nr_modes = ARRAY_SIZE(mode_list_5ghz);
+       }
+
+       for (i = 0; i < nr_modes; i++) {
+               u8 c = ctl_grp | modes[i].ctl_mode;
+               for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++)
+                       if (c == ar->eeprom.ctl_index[ctl_idx])
+                               break;
+               if (ctl_idx < AR5416_NUM_CTLS) {
+                       int f_off = 0;
+
+                       /*
+                        * determine heavy clip parameter
+                        * from the 11G edges array
+                        */
+                       if (modes[i].ctl_mode == CTL_11G) {
+                               ar->heavy_clip =
+                                       carl9170_get_heavy_clip(ar,
+                                               freq, bw, EDGES(ctl_idx, 1));
+                       }
+
+                       /* adjust freq for 40MHz */
+                       if (modes[i].ctl_mode == CTL_2GHT40 ||
+                           modes[i].ctl_mode == CTL_5GHT40) {
+                               if (bw == CARL9170_BW_40_BELOW)
+                                       f_off = -10;
+                               else
+                                       f_off = 10;
+                       }
+
+                       modes[i].max_power =
+                               carl9170_get_max_edge_power(ar,
+                                       freq+f_off, EDGES(ctl_idx, 1));
+
+                       /*
+                        * TODO: check if the regulatory max. power is
+                        * controlled by cfg80211 for DFS.
+                        * (hpmain applies it to max_power itself for DFS freq)
+                        */
+
+               } else {
+                       /*
+                        * Workaround in otus driver, hpmain.c, line 3906:
+                        * if no data for 5GHT20 are found, take the
+                        * legacy 5G value. We extend this here to fallback
+                        * from any other HT* or 11G, too.
+                        */
+                       int k = i;
+
+                       modes[i].max_power = AR5416_MAX_RATE_POWER;
+                       while (k-- > 0) {
+                               if (modes[k].max_power !=
+                                   AR5416_MAX_RATE_POWER) {
+                                       modes[i].max_power = modes[k].max_power;
+                                       break;
+                               }
+                       }
+               }
+
+               /* apply max power to pwr_cal_data (ar->power_*) */
+               for (j = 0; j < modes[i].pwr_cal_len; j++) {
+                       modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j],
+                                                      modes[i].max_power);
+               }
+       }
+
+       if (ar->heavy_clip & 0xf0) {
+               ar->power_2G_ht40[0]--;
+               ar->power_2G_ht40[1]--;
+               ar->power_2G_ht40[2]--;
+       }
+       if (ar->heavy_clip & 0xf) {
+               ar->power_2G_ht20[0]++;
+               ar->power_2G_ht20[1]++;
+               ar->power_2G_ht20[2]++;
+       }
+
+#undef EDGES
+}
+
+static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
+                                 enum carl9170_bw bw)
+{
+       struct ar9170_calibration_target_power_legacy *ctpl;
+       struct ar9170_calibration_target_power_ht *ctph;
+       u8 *ctpres;
+       int ntargets;
+       int idx, i, n;
+       u8 ackpower, ackchains, f;
+       u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+       if (freq < 3000)
+               f = freq - 2300;
+       else
+               f = (freq - 4800)/5;
+
+       /*
+        * cycle through the various modes
+        *
+        * legacy modes first: 5G, 2G CCK, 2G OFDM
+        */
+       for (i = 0; i < 3; i++) {
+               switch (i) {
+               case 0: /* 5 GHz legacy */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_leg;
+                       break;
+               case 1: /* 2.4 GHz CCK */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+                       ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+                       ctpres = ar->power_2G_cck;
+                       break;
+               case 2: /* 2.4 GHz OFDM */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ofdm;
+                       break;
+               default:
+                       BUG();
+               }
+
+               for (n = 0; n < ntargets; n++) {
+                       if (ctpl[n].freq == 0xff)
+                               break;
+                       pwr_freqs[n] = ctpl[n].freq;
+               }
+               ntargets = n;
+               idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
+               for (n = 0; n < 4; n++)
+                       ctpres[n] = carl9170_interpolate_u8(f,
+                               ctpl[idx + 0].freq, ctpl[idx + 0].power[n],
+                               ctpl[idx + 1].freq, ctpl[idx + 1].power[n]);
+       }
+
+       /* HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40 */
+       for (i = 0; i < 4; i++) {
+               switch (i) {
+               case 0: /* 5 GHz HT 20 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_ht20;
+                       break;
+               case 1: /* 5 GHz HT 40 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_ht40;
+                       break;
+               case 2: /* 2.4 GHz HT 20 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ht20;
+                       break;
+               case 3: /* 2.4 GHz HT 40 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ht40;
+                       break;
+               default:
+                       BUG();
+               }
+
+               for (n = 0; n < ntargets; n++) {
+                       if (ctph[n].freq == 0xff)
+                               break;
+                       pwr_freqs[n] = ctph[n].freq;
+               }
+               ntargets = n;
+               idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
+               for (n = 0; n < 8; n++)
+                       ctpres[n] = carl9170_interpolate_u8(f,
+                               ctph[idx + 0].freq, ctph[idx + 0].power[n],
+                               ctph[idx + 1].freq, ctph[idx + 1].power[n]);
+       }
+
+       /* calc. conformance test limits and apply to ar->power*[] */
+       carl9170_calc_ctl(ar, freq, bw);
+
+       /* set ACK/CTS TX power */
+       carl9170_regwrite_begin(ar);
+
+       if (ar->eeprom.tx_mask != 1)
+               ackchains = AR9170_TX_PHY_TXCHAIN_2;
+       else
+               ackchains = AR9170_TX_PHY_TXCHAIN_1;
+
+       if (freq < 3000)
+               ackpower = ar->power_2G_ofdm[0] & 0x3f;
+       else
+               ackpower = ar->power_5G_leg[0] & 0x3f;
+
+       carl9170_regwrite(AR9170_MAC_REG_ACK_TPC,
+                         0x3c1e | ackpower << 20 | ackchains << 26);
+       carl9170_regwrite(AR9170_MAC_REG_RTS_CTS_TPC,
+                         ackpower << 5 | ackchains << 11 |
+                         ackpower << 21 | ackchains << 27);
+
+       carl9170_regwrite(AR9170_MAC_REG_CFEND_QOSNULL_TPC,
+                         ackpower << 5 | ackchains << 11 |
+                         ackpower << 21 | ackchains << 27);
+
+       carl9170_regwrite_finish();
+       return carl9170_regwrite_result();
+}
+
+/* TODO: replace this with sign_extend32(noise, 8) */
+static int carl9170_calc_noise_dbm(u32 raw_noise)
+{
+       if (raw_noise & 0x100)
+               return ~0x1ff | raw_noise;
+       else
+               return raw_noise;
+}
+
+int carl9170_get_noisefloor(struct ar9170 *ar)
+{
+       static const u32 phy_regs[] = {
+               AR9170_PHY_REG_CCA, AR9170_PHY_REG_CH2_CCA,
+               AR9170_PHY_REG_EXT_CCA, AR9170_PHY_REG_CH2_EXT_CCA };
+       u32 phy_res[ARRAY_SIZE(phy_regs)];
+       int err, i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(phy_regs) != ARRAY_SIZE(ar->noise));
+
+       err = carl9170_read_mreg(ar, ARRAY_SIZE(phy_regs), phy_regs, phy_res);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2; i++) {
+               ar->noise[i] = carl9170_calc_noise_dbm(
+                       (phy_res[i] >> 19) & 0x1ff);
+
+               ar->noise[i + 2] = carl9170_calc_noise_dbm(
+                       (phy_res[i + 2] >> 23) & 0x1ff);
+       }
+
+       return 0;
+}
+
+static enum carl9170_bw nl80211_to_carl(enum nl80211_channel_type type)
+{
+       switch (type) {
+       case NL80211_CHAN_NO_HT:
+       case NL80211_CHAN_HT20:
+               return CARL9170_BW_20;
+       case NL80211_CHAN_HT40MINUS:
+               return CARL9170_BW_40_BELOW;
+       case NL80211_CHAN_HT40PLUS:
+               return CARL9170_BW_40_ABOVE;
+       default:
+               BUG();
+       }
+}
+
+int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+                        enum nl80211_channel_type _bw,
+                        enum carl9170_rf_init_mode rfi)
+{
+       const struct carl9170_phy_freq_params *freqpar;
+       struct carl9170_rf_init_result rf_res;
+       struct carl9170_rf_init rf;
+       u32 cmd, tmp, offs = 0, new_ht = 0;
+       int err;
+       enum carl9170_bw bw;
+       bool warm_reset;
+       struct ieee80211_channel *old_channel = NULL;
+
+       bw = nl80211_to_carl(_bw);
+
+       if (conf_is_ht(&ar->hw->conf))
+               new_ht |= CARL9170FW_PHY_HT_ENABLE;
+
+       if (conf_is_ht40(&ar->hw->conf))
+               new_ht |= CARL9170FW_PHY_HT_DYN2040;
+
+       /* may be NULL at first setup */
+       if (ar->channel) {
+               old_channel = ar->channel;
+               warm_reset = (old_channel->band != channel->band) ||
+                            (old_channel->center_freq ==
+                             channel->center_freq) ||
+                            (ar->ht_settings != new_ht);
+
+               ar->channel = NULL;
+       } else {
+               warm_reset = true;
+       }
+
+       /* HW workaround */
+       if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
+           channel->center_freq <= 2417)
+               warm_reset = true;
+
+       if (rfi != CARL9170_RFI_NONE || warm_reset) {
+               u32 val;
+
+               if (rfi == CARL9170_RFI_COLD)
+                       val = AR9170_PWR_RESET_BB_COLD_RESET;
+               else
+                       val = AR9170_PWR_RESET_BB_WARM_RESET;
+
+               /* warm/cold reset BB/ADDA */
+               err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, val);
+               if (err)
+                       return err;
+
+               err = carl9170_write_reg(ar, AR9170_PWR_REG_RESET, 0x0);
+               if (err)
+                       return err;
+
+               err = carl9170_init_phy(ar, channel->band);
+               if (err)
+                       return err;
+
+               err = carl9170_init_rf_banks_0_7(ar,
+                       channel->band == IEEE80211_BAND_5GHZ);
+               if (err)
+                       return err;
+
+               cmd = CARL9170_CMD_RF_INIT;
+
+               msleep(100);
+
+               err = carl9170_echo_test(ar, 0xaabbccdd);
+               if (err)
+                       return err;
+       } else {
+               cmd = CARL9170_CMD_FREQUENCY;
+       }
+
+       err = carl9170_exec_cmd(ar, CARL9170_CMD_FREQ_START, 0, NULL, 0, NULL);
+       if (err)
+               return err;
+
+       err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
+                                0x200);
+
+       err = carl9170_init_rf_bank4_pwr(ar,
+               channel->band == IEEE80211_BAND_5GHZ,
+               channel->center_freq, bw);
+       if (err)
+               return err;
+
+       tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 |
+             AR9170_PHY_TURBO_FC_HT_EN;
+
+       switch (bw) {
+       case CARL9170_BW_20:
+               break;
+       case CARL9170_BW_40_BELOW:
+               tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN |
+                      AR9170_PHY_TURBO_FC_SHORT_GI_40;
+               offs = 3;
+               break;
+       case CARL9170_BW_40_ABOVE:
+               tmp |= AR9170_PHY_TURBO_FC_DYN2040_EN |
+                      AR9170_PHY_TURBO_FC_SHORT_GI_40 |
+                      AR9170_PHY_TURBO_FC_DYN2040_PRI_CH;
+               offs = 1;
+               break;
+       default:
+               BUG();
+               return -ENOSYS;
+       }
+
+       if (ar->eeprom.tx_mask != 1)
+               tmp |= AR9170_PHY_TURBO_FC_WALSH;
+
+       err = carl9170_write_reg(ar, AR9170_PHY_REG_TURBO, tmp);
+       if (err)
+               return err;
+
+       err = carl9170_set_freq_cal_data(ar, channel);
+       if (err)
+               return err;
+
+       err = carl9170_set_power_cal(ar, channel->center_freq, bw);
+       if (err)
+               return err;
+
+       freqpar = carl9170_get_hw_dyn_params(channel, bw);
+
+       rf.ht_settings = new_ht;
+       if (conf_is_ht40(&ar->hw->conf))
+               SET_VAL(CARL9170FW_PHY_HT_EXT_CHAN_OFF, rf.ht_settings, offs);
+
+       rf.freq = cpu_to_le32(channel->center_freq * 1000);
+       rf.delta_slope_coeff_exp = cpu_to_le32(freqpar->coeff_exp);
+       rf.delta_slope_coeff_man = cpu_to_le32(freqpar->coeff_man);
+       rf.delta_slope_coeff_exp_shgi = cpu_to_le32(freqpar->coeff_exp_shgi);
+       rf.delta_slope_coeff_man_shgi = cpu_to_le32(freqpar->coeff_man_shgi);
+
+       if (rfi != CARL9170_RFI_NONE)
+               rf.finiteLoopCount = cpu_to_le32(2000);
+       else
+               rf.finiteLoopCount = cpu_to_le32(1000);
+
+       err = carl9170_exec_cmd(ar, cmd, sizeof(rf), &rf,
+                               sizeof(rf_res), &rf_res);
+       if (err)
+               return err;
+
+       err = le32_to_cpu(rf_res.ret);
+       if (err != 0) {
+               ar->chan_fail++;
+               ar->total_chan_fail++;
+
+               wiphy_err(ar->hw->wiphy, "channel change: %d -> %d "
+                         "failed (%d).\n", old_channel ?
+                         old_channel->center_freq : -1, channel->center_freq,
+                         err);
+
+               if ((rfi == CARL9170_RFI_COLD) || (ar->chan_fail > 3)) {
+                       /*
+                        * We have tried very hard to change to _another_
+                        * channel and we've failed to do so!
+                        * Chances are that the PHY/RF is no longer
+                        * operable (due to corruptions/fatal events/bugs?)
+                        * and we need to reset at a higher level.
+                        */
+                       carl9170_restart(ar, CARL9170_RR_TOO_MANY_PHY_ERRORS);
+                       return 0;
+               }
+
+               err = carl9170_set_channel(ar, channel, _bw,
+                                          CARL9170_RFI_COLD);
+               if (err)
+                       return err;
+       } else {
+               ar->chan_fail = 0;
+       }
+
+       err = carl9170_get_noisefloor(ar);
+       if (err)
+               return err;
+
+       if (ar->heavy_clip) {
+               err = carl9170_write_reg(ar, AR9170_PHY_REG_HEAVY_CLIP_ENABLE,
+                                        0x200 | ar->heavy_clip);
+               if (err) {
+                       if (net_ratelimit()) {
+                               wiphy_err(ar->hw->wiphy, "failed to set "
+                                      "heavy clip\n");
+                       }
+
+                       return err;
+               }
+       }
+
+       /* FIXME: PSM does not work in 5GHz Band */
+       if (channel->band == IEEE80211_BAND_5GHZ)
+               ar->ps.off_override |= PS_OFF_5GHZ;
+       else
+               ar->ps.off_override &= ~PS_OFF_5GHZ;
+
+       ar->channel = channel;
+       ar->ht_settings = new_ht;
+       return 0;
+}
diff --git a/drivers/net/wireless/ath/carl9170/phy.h b/drivers/net/wireless/ath/carl9170/phy.h
new file mode 100644 (file)
index 0000000..53c18d3
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * PHY register map
+ *
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or 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 __CARL9170_SHARED_PHY_H
+#define __CARL9170_SHARED_PHY_H
+
+#define        AR9170_PHY_REG_BASE                     (0x1bc000 + 0x9800)
+#define        AR9170_PHY_REG(_n)                      (AR9170_PHY_REG_BASE + \
+                                                ((_n) << 2))
+
+#define        AR9170_PHY_REG_TEST                     (AR9170_PHY_REG_BASE + 0x0000)
+#define                AR9170_PHY_TEST_AGC_CLR                 0x10000000
+#define                AR9170_PHY_TEST_RFSILENT_BB             0x00002000
+
+#define        AR9170_PHY_REG_TURBO                    (AR9170_PHY_REG_BASE + 0x0004)
+#define                AR9170_PHY_TURBO_FC_TURBO_MODE          0x00000001
+#define                AR9170_PHY_TURBO_FC_TURBO_SHORT         0x00000002
+#define                AR9170_PHY_TURBO_FC_DYN2040_EN          0x00000004
+#define                AR9170_PHY_TURBO_FC_DYN2040_PRI_ONLY    0x00000008
+#define                AR9170_PHY_TURBO_FC_DYN2040_PRI_CH      0x00000010
+/* For 25 MHz channel spacing -- not used but supported by hw */
+#define                AR9170_PHY_TURBO_FC_DYN2040_EXT_CH      0x00000020
+#define                AR9170_PHY_TURBO_FC_HT_EN               0x00000040
+#define                AR9170_PHY_TURBO_FC_SHORT_GI_40         0x00000080
+#define                AR9170_PHY_TURBO_FC_WALSH               0x00000100
+#define                AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1      0x00000200
+#define                AR9170_PHY_TURBO_FC_ENABLE_DAC_FIFO     0x00000800
+
+#define        AR9170_PHY_REG_TEST2                    (AR9170_PHY_REG_BASE + 0x0008)
+
+#define        AR9170_PHY_REG_TIMING2                  (AR9170_PHY_REG_BASE + 0x0010)
+#define                AR9170_PHY_TIMING2_USE_FORCE            0x00001000
+#define                AR9170_PHY_TIMING2_FORCE                0x00000fff
+#define                AR9170_PHY_TIMING2_FORCE_S                       0
+
+#define        AR9170_PHY_REG_TIMING3                  (AR9170_PHY_REG_BASE + 0x0014)
+#define                AR9170_PHY_TIMING3_DSC_EXP              0x0001e000
+#define                AR9170_PHY_TIMING3_DSC_EXP_S            13
+#define                AR9170_PHY_TIMING3_DSC_MAN              0xfffe0000
+#define                AR9170_PHY_TIMING3_DSC_MAN_S            17
+
+#define        AR9170_PHY_REG_CHIP_ID                  (AR9170_PHY_REG_BASE + 0x0018)
+#define                AR9170_PHY_CHIP_ID_REV_0                0x80
+#define                AR9170_PHY_CHIP_ID_REV_1                0x81
+#define                AR9170_PHY_CHIP_ID_9160_REV_0           0xb0
+
+#define        AR9170_PHY_REG_ACTIVE                   (AR9170_PHY_REG_BASE + 0x001c)
+#define                AR9170_PHY_ACTIVE_EN                    0x00000001
+#define                AR9170_PHY_ACTIVE_DIS                   0x00000000
+
+#define        AR9170_PHY_REG_RF_CTL2                  (AR9170_PHY_REG_BASE + 0x0024)
+#define                AR9170_PHY_RF_CTL2_TX_END_DATA_START    0x000000ff
+#define                AR9170_PHY_RF_CTL2_TX_END_DATA_START_S  0
+#define                AR9170_PHY_RF_CTL2_TX_END_PA_ON         0x0000ff00
+#define                AR9170_PHY_RF_CTL2_TX_END_PA_ON_S       8
+
+#define        AR9170_PHY_REG_RF_CTL3                  (AR9170_PHY_REG_BASE + 0x0028)
+#define                AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON   0x00ff0000
+#define                AR9170_PHY_RF_CTL3_TX_END_TO_A2_RX_ON_S 16
+
+#define        AR9170_PHY_REG_ADC_CTL                  (AR9170_PHY_REG_BASE + 0x002c)
+#define                AR9170_PHY_ADC_CTL_OFF_INBUFGAIN        0x00000003
+#define                AR9170_PHY_ADC_CTL_OFF_INBUFGAIN_S      0
+#define                AR9170_PHY_ADC_CTL_OFF_PWDDAC           0x00002000
+#define                AR9170_PHY_ADC_CTL_OFF_PWDBANDGAP       0x00004000
+#define                AR9170_PHY_ADC_CTL_OFF_PWDADC           0x00008000
+#define                AR9170_PHY_ADC_CTL_ON_INBUFGAIN         0x00030000
+#define                AR9170_PHY_ADC_CTL_ON_INBUFGAIN_S       16
+
+#define        AR9170_PHY_REG_ADC_SERIAL_CTL           (AR9170_PHY_REG_BASE + 0x0030)
+#define                AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC  0x00000000
+#define                AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO  0x00000001
+
+#define        AR9170_PHY_REG_RF_CTL4                  (AR9170_PHY_REG_BASE + 0x0034)
+#define                AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF      0xff000000
+#define                AR9170_PHY_RF_CTL4_TX_END_XPAB_OFF_S    24
+#define                AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF      0x00ff0000
+#define                AR9170_PHY_RF_CTL4_TX_END_XPAA_OFF_S    16
+#define                AR9170_PHY_RF_CTL4_FRAME_XPAB_ON        0x0000ff00
+#define                AR9170_PHY_RF_CTL4_FRAME_XPAB_ON_S      8
+#define                AR9170_PHY_RF_CTL4_FRAME_XPAA_ON        0x000000ff
+#define                AR9170_PHY_RF_CTL4_FRAME_XPAA_ON_S      0
+
+#define        AR9170_PHY_REG_TSTDAC_CONST             (AR9170_PHY_REG_BASE + 0x003c)
+
+#define        AR9170_PHY_REG_SETTLING                 (AR9170_PHY_REG_BASE + 0x0044)
+#define                AR9170_PHY_SETTLING_SWITCH              0x00003f80
+#define                AR9170_PHY_SETTLING_SWITCH_S            7
+
+#define        AR9170_PHY_REG_RXGAIN                   (AR9170_PHY_REG_BASE + 0x0048)
+#define        AR9170_PHY_REG_RXGAIN_CHAIN_2           (AR9170_PHY_REG_BASE + 0x2048)
+#define                AR9170_PHY_RXGAIN_TXRX_ATTEN            0x0003f000
+#define                AR9170_PHY_RXGAIN_TXRX_ATTEN_S          12
+#define                AR9170_PHY_RXGAIN_TXRX_RF_MAX           0x007c0000
+#define                AR9170_PHY_RXGAIN_TXRX_RF_MAX_S         18
+
+#define        AR9170_PHY_REG_DESIRED_SZ               (AR9170_PHY_REG_BASE + 0x0050)
+#define                AR9170_PHY_DESIRED_SZ_ADC               0x000000ff
+#define                AR9170_PHY_DESIRED_SZ_ADC_S             0
+#define                AR9170_PHY_DESIRED_SZ_PGA               0x0000ff00
+#define                AR9170_PHY_DESIRED_SZ_PGA_S             8
+#define                AR9170_PHY_DESIRED_SZ_TOT_DES           0x0ff00000
+#define                AR9170_PHY_DESIRED_SZ_TOT_DES_S         20
+
+#define        AR9170_PHY_REG_FIND_SIG                 (AR9170_PHY_REG_BASE + 0x0058)
+#define                AR9170_PHY_FIND_SIG_FIRSTEP             0x0003f000
+#define                AR9170_PHY_FIND_SIG_FIRSTEP_S           12
+#define                AR9170_PHY_FIND_SIG_FIRPWR              0x03fc0000
+#define                AR9170_PHY_FIND_SIG_FIRPWR_S            18
+
+#define        AR9170_PHY_REG_AGC_CTL1                 (AR9170_PHY_REG_BASE + 0x005c)
+#define                AR9170_PHY_AGC_CTL1_COARSE_LOW          0x00007f80
+#define                AR9170_PHY_AGC_CTL1_COARSE_LOW_S        7
+#define                AR9170_PHY_AGC_CTL1_COARSE_HIGH         0x003f8000
+#define                AR9170_PHY_AGC_CTL1_COARSE_HIGH_S       15
+
+#define        AR9170_PHY_REG_AGC_CONTROL              (AR9170_PHY_REG_BASE + 0x0060)
+#define                AR9170_PHY_AGC_CONTROL_CAL              0x00000001
+#define                AR9170_PHY_AGC_CONTROL_NF               0x00000002
+#define                AR9170_PHY_AGC_CONTROL_ENABLE_NF        0x00008000
+#define                AR9170_PHY_AGC_CONTROL_FLTR_CAL         0x00010000
+#define                AR9170_PHY_AGC_CONTROL_NO_UPDATE_NF     0x00020000
+
+#define        AR9170_PHY_REG_CCA                      (AR9170_PHY_REG_BASE + 0x0064)
+#define                AR9170_PHY_CCA_MINCCA_PWR               0x0ff80000
+#define                AR9170_PHY_CCA_MINCCA_PWR_S             19
+#define                AR9170_PHY_CCA_THRESH62                 0x0007f000
+#define                AR9170_PHY_CCA_THRESH62_S               12
+
+#define        AR9170_PHY_REG_SFCORR                   (AR9170_PHY_REG_BASE + 0x0068)
+#define                AR9170_PHY_SFCORR_M2COUNT_THR           0x0000001f
+#define                AR9170_PHY_SFCORR_M2COUNT_THR_S         0
+#define                AR9170_PHY_SFCORR_M1_THRESH             0x00fe0000
+#define                AR9170_PHY_SFCORR_M1_THRESH_S           17
+#define                AR9170_PHY_SFCORR_M2_THRESH             0x7f000000
+#define                AR9170_PHY_SFCORR_M2_THRESH_S           24
+
+#define        AR9170_PHY_REG_SFCORR_LOW               (AR9170_PHY_REG_BASE + 0x006c)
+#define                AR9170_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001
+#define                AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW   0x00003f00
+#define                AR9170_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8
+#define                AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW     0x001fc000
+#define                AR9170_PHY_SFCORR_LOW_M1_THRESH_LOW_S   14
+#define                AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW     0x0fe00000
+#define                AR9170_PHY_SFCORR_LOW_M2_THRESH_LOW_S   21
+
+#define        AR9170_PHY_REG_SLEEP_CTR_CONTROL        (AR9170_PHY_REG_BASE + 0x0070)
+#define        AR9170_PHY_REG_SLEEP_CTR_LIMIT          (AR9170_PHY_REG_BASE + 0x0074)
+#define        AR9170_PHY_REG_SLEEP_SCAL               (AR9170_PHY_REG_BASE + 0x0078)
+
+#define        AR9170_PHY_REG_PLL_CTL                  (AR9170_PHY_REG_BASE + 0x007c)
+#define                AR9170_PHY_PLL_CTL_40                   0xaa
+#define                AR9170_PHY_PLL_CTL_40_5413              0x04
+#define                AR9170_PHY_PLL_CTL_44                   0xab
+#define                AR9170_PHY_PLL_CTL_44_2133              0xeb
+#define                AR9170_PHY_PLL_CTL_40_2133              0xea
+
+#define        AR9170_PHY_REG_BIN_MASK_1               (AR9170_PHY_REG_BASE + 0x0100)
+#define        AR9170_PHY_REG_BIN_MASK_2               (AR9170_PHY_REG_BASE + 0x0104)
+#define        AR9170_PHY_REG_BIN_MASK_3               (AR9170_PHY_REG_BASE + 0x0108)
+#define        AR9170_PHY_REG_MASK_CTL                 (AR9170_PHY_REG_BASE + 0x010c)
+
+/* analogue power on time (100ns) */
+#define        AR9170_PHY_REG_RX_DELAY                 (AR9170_PHY_REG_BASE + 0x0114)
+#define        AR9170_PHY_REG_SEARCH_START_DELAY       (AR9170_PHY_REG_BASE + 0x0118)
+#define                AR9170_PHY_RX_DELAY_DELAY               0x00003fff
+
+#define        AR9170_PHY_REG_TIMING_CTRL4(_i)         (AR9170_PHY_REG_BASE + \
+                                               (0x0120 + ((_i) << 12)))
+#define                AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF         0x01f
+#define                AR9170_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S       0
+#define                AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF         0x7e0
+#define                AR9170_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S       5
+#define                AR9170_PHY_TIMING_CTRL4_IQCORR_ENABLE           0x800
+#define                AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX     0xf000
+#define                AR9170_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S   12
+#define                AR9170_PHY_TIMING_CTRL4_DO_IQCAL                0x10000
+#define                AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI        0x80000000
+#define                AR9170_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER      0x40000000
+#define                AR9170_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK        0x20000000
+#define                AR9170_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK       0x10000000
+
+#define        AR9170_PHY_REG_TIMING5                  (AR9170_PHY_REG_BASE + 0x0124)
+#define                AR9170_PHY_TIMING5_CYCPWR_THR1          0x000000fe
+#define                AR9170_PHY_TIMING5_CYCPWR_THR1_S        1
+
+#define        AR9170_PHY_REG_POWER_TX_RATE1           (AR9170_PHY_REG_BASE + 0x0134)
+#define        AR9170_PHY_REG_POWER_TX_RATE2           (AR9170_PHY_REG_BASE + 0x0138)
+#define        AR9170_PHY_REG_POWER_TX_RATE_MAX        (AR9170_PHY_REG_BASE + 0x013c)
+#define                AR9170_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040
+
+#define        AR9170_PHY_REG_FRAME_CTL                (AR9170_PHY_REG_BASE + 0x0144)
+#define                AR9170_PHY_FRAME_CTL_TX_CLIP            0x00000038
+#define                AR9170_PHY_FRAME_CTL_TX_CLIP_S          3
+
+#define        AR9170_PHY_REG_SPUR_REG                 (AR9170_PHY_REG_BASE + 0x014c)
+#define                AR9170_PHY_SPUR_REG_MASK_RATE_CNTL      (0xff << 18)
+#define                AR9170_PHY_SPUR_REG_MASK_RATE_CNTL_S    18
+#define                AR9170_PHY_SPUR_REG_ENABLE_MASK_PPM     0x20000
+#define                AR9170_PHY_SPUR_REG_MASK_RATE_SELECT    (0xff << 9)
+#define                AR9170_PHY_SPUR_REG_MASK_RATE_SELECT_S  9
+#define                AR9170_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI        0x100
+#define                AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH    0x7f
+#define                AR9170_PHY_SPUR_REG_SPUR_RSSI_THRESH_S  0
+
+#define        AR9170_PHY_REG_RADAR_EXT                (AR9170_PHY_REG_BASE + 0x0140)
+#define                AR9170_PHY_RADAR_EXT_ENA                0x00004000
+
+#define        AR9170_PHY_REG_RADAR_0                  (AR9170_PHY_REG_BASE + 0x0154)
+#define                AR9170_PHY_RADAR_0_ENA                  0x00000001
+#define                AR9170_PHY_RADAR_0_FFT_ENA              0x80000000
+/* inband pulse threshold */
+#define                AR9170_PHY_RADAR_0_INBAND               0x0000003e
+#define                AR9170_PHY_RADAR_0_INBAND_S             1
+/* pulse RSSI threshold */
+#define                AR9170_PHY_RADAR_0_PRSSI                0x00000fc0
+#define                AR9170_PHY_RADAR_0_PRSSI_S              6
+/* pulse height threshold */
+#define                AR9170_PHY_RADAR_0_HEIGHT               0x0003f000
+#define                AR9170_PHY_RADAR_0_HEIGHT_S             12
+/* radar RSSI threshold */
+#define                AR9170_PHY_RADAR_0_RRSSI                0x00fc0000
+#define                AR9170_PHY_RADAR_0_RRSSI_S              18
+/* radar firepower threshold */
+#define                AR9170_PHY_RADAR_0_FIRPWR               0x7f000000
+#define                AR9170_PHY_RADAR_0_FIRPWR_S             24
+
+#define        AR9170_PHY_REG_RADAR_1                  (AR9170_PHY_REG_BASE + 0x0158)
+#define                AR9170_PHY_RADAR_1_RELPWR_ENA           0x00800000
+#define                AR9170_PHY_RADAR_1_USE_FIR128           0x00400000
+#define                AR9170_PHY_RADAR_1_RELPWR_THRESH        0x003f0000
+#define                AR9170_PHY_RADAR_1_RELPWR_THRESH_S      16
+#define                AR9170_PHY_RADAR_1_BLOCK_CHECK          0x00008000
+#define                AR9170_PHY_RADAR_1_MAX_RRSSI            0x00004000
+#define                AR9170_PHY_RADAR_1_RELSTEP_CHECK        0x00002000
+#define                AR9170_PHY_RADAR_1_RELSTEP_THRESH       0x00001f00
+#define                AR9170_PHY_RADAR_1_RELSTEP_THRESH_S     8
+#define                AR9170_PHY_RADAR_1_MAXLEN               0x000000ff
+#define                AR9170_PHY_RADAR_1_MAXLEN_S             0
+
+#define        AR9170_PHY_REG_SWITCH_CHAIN_0           (AR9170_PHY_REG_BASE + 0x0160)
+#define        AR9170_PHY_REG_SWITCH_CHAIN_2           (AR9170_PHY_REG_BASE + 0x2160)
+
+#define        AR9170_PHY_REG_SWITCH_COM               (AR9170_PHY_REG_BASE + 0x0164)
+
+#define        AR9170_PHY_REG_CCA_THRESHOLD            (AR9170_PHY_REG_BASE + 0x0168)
+
+#define        AR9170_PHY_REG_SIGMA_DELTA              (AR9170_PHY_REG_BASE + 0x016c)
+#define                AR9170_PHY_SIGMA_DELTA_ADC_SEL          0x00000003
+#define                AR9170_PHY_SIGMA_DELTA_ADC_SEL_S        0
+#define                AR9170_PHY_SIGMA_DELTA_FILT2            0x000000f8
+#define                AR9170_PHY_SIGMA_DELTA_FILT2_S          3
+#define                AR9170_PHY_SIGMA_DELTA_FILT1            0x00001f00
+#define                AR9170_PHY_SIGMA_DELTA_FILT1_S          8
+#define                AR9170_PHY_SIGMA_DELTA_ADC_CLIP         0x01ffe000
+#define                AR9170_PHY_SIGMA_DELTA_ADC_CLIP_S       13
+
+#define        AR9170_PHY_REG_RESTART                  (AR9170_PHY_REG_BASE + 0x0170)
+#define                AR9170_PHY_RESTART_DIV_GC               0x001c0000
+#define                AR9170_PHY_RESTART_DIV_GC_S             18
+
+#define        AR9170_PHY_REG_RFBUS_REQ                (AR9170_PHY_REG_BASE + 0x017c)
+#define                AR9170_PHY_RFBUS_REQ_EN                 0x00000001
+
+#define        AR9170_PHY_REG_TIMING7                  (AR9170_PHY_REG_BASE + 0x0180)
+#define        AR9170_PHY_REG_TIMING8                  (AR9170_PHY_REG_BASE + 0x0184)
+#define                AR9170_PHY_TIMING8_PILOT_MASK_2         0x000fffff
+#define                AR9170_PHY_TIMING8_PILOT_MASK_2_S       0
+
+#define        AR9170_PHY_REG_BIN_MASK2_1              (AR9170_PHY_REG_BASE + 0x0188)
+#define        AR9170_PHY_REG_BIN_MASK2_2              (AR9170_PHY_REG_BASE + 0x018c)
+#define        AR9170_PHY_REG_BIN_MASK2_3              (AR9170_PHY_REG_BASE + 0x0190)
+#define        AR9170_PHY_REG_BIN_MASK2_4              (AR9170_PHY_REG_BASE + 0x0194)
+#define                AR9170_PHY_BIN_MASK2_4_MASK_4           0x00003fff
+#define                AR9170_PHY_BIN_MASK2_4_MASK_4_S         0
+
+#define        AR9170_PHY_REG_TIMING9                  (AR9170_PHY_REG_BASE + 0x0198)
+#define        AR9170_PHY_REG_TIMING10                 (AR9170_PHY_REG_BASE + 0x019c)
+#define                AR9170_PHY_TIMING10_PILOT_MASK_2        0x000fffff
+#define                AR9170_PHY_TIMING10_PILOT_MASK_2_S      0
+
+#define        AR9170_PHY_REG_TIMING11                 (AR9170_PHY_REG_BASE + 0x01a0)
+#define                AR9170_PHY_TIMING11_SPUR_DELTA_PHASE    0x000fffff
+#define                AR9170_PHY_TIMING11_SPUR_DELTA_PHASE_S  0
+#define                AR9170_PHY_TIMING11_SPUR_FREQ_SD        0x3ff00000
+#define                AR9170_PHY_TIMING11_SPUR_FREQ_SD_S      20
+#define                AR9170_PHY_TIMING11_USE_SPUR_IN_AGC     0x40000000
+#define                AR9170_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000
+
+#define        AR9170_PHY_REG_RX_CHAINMASK             (AR9170_PHY_REG_BASE + 0x01a4)
+#define        AR9170_PHY_REG_NEW_ADC_DC_GAIN_CORR(_i) (AR9170_PHY_REG_BASE + \
+                                                0x01b4 + ((_i) << 12))
+#define                AR9170_PHY_NEW_ADC_GAIN_CORR_ENABLE             0x40000000
+#define                AR9170_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE        0x80000000
+
+#define        AR9170_PHY_REG_MULTICHAIN_GAIN_CTL      (AR9170_PHY_REG_BASE + 0x01ac)
+#define                AR9170_PHY_9285_ANT_DIV_CTL_ALL         0x7f000000
+#define                AR9170_PHY_9285_ANT_DIV_CTL             0x01000000
+#define                AR9170_PHY_9285_ANT_DIV_CTL_S           24
+#define                AR9170_PHY_9285_ANT_DIV_ALT_LNACONF     0x06000000
+#define                AR9170_PHY_9285_ANT_DIV_ALT_LNACONF_S   25
+#define                AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF    0x18000000
+#define                AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF_S  27
+#define                AR9170_PHY_9285_ANT_DIV_ALT_GAINTB      0x20000000
+#define                AR9170_PHY_9285_ANT_DIV_ALT_GAINTB_S    29
+#define                AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB     0x40000000
+#define                AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB_S   30
+#define                AR9170_PHY_9285_ANT_DIV_LNA1            2
+#define                AR9170_PHY_9285_ANT_DIV_LNA2            1
+#define                AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2  3
+#define                AR9170_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0
+#define                AR9170_PHY_9285_ANT_DIV_GAINTB_0        0
+#define                AR9170_PHY_9285_ANT_DIV_GAINTB_1        1
+
+#define        AR9170_PHY_REG_EXT_CCA0                 (AR9170_PHY_REG_BASE + 0x01b8)
+#define                AR9170_PHY_REG_EXT_CCA0_THRESH62        0x000000ff
+#define                AR9170_PHY_REG_EXT_CCA0_THRESH62_S      0
+
+#define        AR9170_PHY_REG_EXT_CCA                  (AR9170_PHY_REG_BASE + 0x01bc)
+#define                AR9170_PHY_EXT_CCA_CYCPWR_THR1          0x0000fe00
+#define                AR9170_PHY_EXT_CCA_CYCPWR_THR1_S        9
+#define                AR9170_PHY_EXT_CCA_THRESH62             0x007f0000
+#define                AR9170_PHY_EXT_CCA_THRESH62_S           16
+#define                AR9170_PHY_EXT_MINCCA_PWR               0xff800000
+#define                AR9170_PHY_EXT_MINCCA_PWR_S             23
+
+#define        AR9170_PHY_REG_SFCORR_EXT               (AR9170_PHY_REG_BASE + 0x01c0)
+#define                AR9170_PHY_SFCORR_EXT_M1_THRESH         0x0000007f
+#define                AR9170_PHY_SFCORR_EXT_M1_THRESH_S       0
+#define                AR9170_PHY_SFCORR_EXT_M2_THRESH         0x00003f80
+#define                AR9170_PHY_SFCORR_EXT_M2_THRESH_S       7
+#define                AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW     0x001fc000
+#define                AR9170_PHY_SFCORR_EXT_M1_THRESH_LOW_S   14
+#define                AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW     0x0fe00000
+#define                AR9170_PHY_SFCORR_EXT_M2_THRESH_LOW_S   21
+#define                AR9170_PHY_SFCORR_SPUR_SUBCHNL_SD_S     28
+
+#define        AR9170_PHY_REG_HALFGI                   (AR9170_PHY_REG_BASE + 0x01d0)
+#define                AR9170_PHY_HALFGI_DSC_MAN               0x0007fff0
+#define                AR9170_PHY_HALFGI_DSC_MAN_S             4
+#define                AR9170_PHY_HALFGI_DSC_EXP               0x0000000f
+#define                AR9170_PHY_HALFGI_DSC_EXP_S             0
+
+#define        AR9170_PHY_REG_CHANNEL_MASK_01_30       (AR9170_PHY_REG_BASE + 0x01d4)
+#define        AR9170_PHY_REG_CHANNEL_MASK_31_60       (AR9170_PHY_REG_BASE + 0x01d8)
+
+#define        AR9170_PHY_REG_CHAN_INFO_MEMORY         (AR9170_PHY_REG_BASE + 0x01dc)
+#define                AR9170_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK        0x0001
+
+#define        AR9170_PHY_REG_HEAVY_CLIP_ENABLE        (AR9170_PHY_REG_BASE + 0x01e0)
+#define        AR9170_PHY_REG_HEAVY_CLIP_FACTOR_RIFS   (AR9170_PHY_REG_BASE + 0x01ec)
+#define                AR9170_PHY_RIFS_INIT_DELAY              0x03ff0000
+
+#define        AR9170_PHY_REG_CALMODE                  (AR9170_PHY_REG_BASE + 0x01f0)
+#define                AR9170_PHY_CALMODE_IQ                   0x00000000
+#define                AR9170_PHY_CALMODE_ADC_GAIN             0x00000001
+#define                AR9170_PHY_CALMODE_ADC_DC_PER           0x00000002
+#define                AR9170_PHY_CALMODE_ADC_DC_INIT          0x00000003
+
+#define        AR9170_PHY_REG_REFCLKDLY                (AR9170_PHY_REG_BASE + 0x01f4)
+#define        AR9170_PHY_REG_REFCLKPD                 (AR9170_PHY_REG_BASE + 0x01f8)
+
+
+#define        AR9170_PHY_REG_CAL_MEAS_0(_i)           (AR9170_PHY_REG_BASE + \
+                                                0x0410 + ((_i) << 12))
+#define        AR9170_PHY_REG_CAL_MEAS_1(_i)           (AR9170_PHY_REG_BASE + \
+                                                0x0414 \ + ((_i) << 12))
+#define        AR9170_PHY_REG_CAL_MEAS_2(_i)           (AR9170_PHY_REG_BASE + \
+                                                0x0418 + ((_i) << 12))
+#define        AR9170_PHY_REG_CAL_MEAS_3(_i)           (AR9170_PHY_REG_BASE + \
+                                                0x041c + ((_i) << 12))
+
+#define        AR9170_PHY_REG_CURRENT_RSSI             (AR9170_PHY_REG_BASE + 0x041c)
+
+#define        AR9170_PHY_REG_RFBUS_GRANT              (AR9170_PHY_REG_BASE + 0x0420)
+#define                AR9170_PHY_RFBUS_GRANT_EN               0x00000001
+
+#define        AR9170_PHY_REG_CHAN_INFO_GAIN_DIFF      (AR9170_PHY_REG_BASE + 0x04f4)
+#define                AR9170_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT      320
+
+#define        AR9170_PHY_REG_CHAN_INFO_GAIN           (AR9170_PHY_REG_BASE + 0x04fc)
+
+#define        AR9170_PHY_REG_MODE                     (AR9170_PHY_REG_BASE + 0x0a00)
+#define                AR9170_PHY_MODE_ASYNCFIFO               0x80
+#define                AR9170_PHY_MODE_AR2133                  0x08
+#define                AR9170_PHY_MODE_AR5111                  0x00
+#define                AR9170_PHY_MODE_AR5112                  0x08
+#define                AR9170_PHY_MODE_DYNAMIC                 0x04
+#define                AR9170_PHY_MODE_RF2GHZ                  0x02
+#define                AR9170_PHY_MODE_RF5GHZ                  0x00
+#define                AR9170_PHY_MODE_CCK                     0x01
+#define                AR9170_PHY_MODE_OFDM                    0x00
+#define                AR9170_PHY_MODE_DYN_CCK_DISABLE         0x100
+
+#define        AR9170_PHY_REG_CCK_TX_CTRL              (AR9170_PHY_REG_BASE + 0x0a04)
+#define                AR9170_PHY_CCK_TX_CTRL_JAPAN                    0x00000010
+#define                AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK         0x0000000c
+#define                AR9170_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S       2
+
+#define        AR9170_PHY_REG_CCK_DETECT               (AR9170_PHY_REG_BASE + 0x0a08)
+#define                AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK          0x0000003f
+#define                AR9170_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S        0
+/* [12:6] settling time for antenna switch */
+#define                AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME           0x00001fc0
+#define                AR9170_PHY_CCK_DETECT_ANT_SWITCH_TIME_S         6
+#define                AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV    0x2000
+#define                AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S  13
+
+#define        AR9170_PHY_REG_GAIN_2GHZ_CHAIN_2        (AR9170_PHY_REG_BASE + 0x2a0c)
+#define        AR9170_PHY_REG_GAIN_2GHZ                (AR9170_PHY_REG_BASE + 0x0a0c)
+#define                AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN        0x00fc0000
+#define                AR9170_PHY_GAIN_2GHZ_RXTX_MARGIN_S      18
+#define                AR9170_PHY_GAIN_2GHZ_BSW_MARGIN         0x00003c00
+#define                AR9170_PHY_GAIN_2GHZ_BSW_MARGIN_S       10
+#define                AR9170_PHY_GAIN_2GHZ_BSW_ATTEN          0x0000001f
+#define                AR9170_PHY_GAIN_2GHZ_BSW_ATTEN_S        0
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN     0x003e0000
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S   17
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN     0x0001f000
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S   12
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN2_DB         0x00000fc0
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN2_DB_S       6
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN1_DB         0x0000003f
+#define                AR9170_PHY_GAIN_2GHZ_XATTEN1_DB_S       0
+
+#define        AR9170_PHY_REG_CCK_RXCTRL4              (AR9170_PHY_REG_BASE + 0x0a1c)
+#define                AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT   0x01f80000
+#define                AR9170_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19
+
+#define        AR9170_PHY_REG_DAG_CTRLCCK              (AR9170_PHY_REG_BASE + 0x0a28)
+#define                AR9170_REG_DAG_CTRLCCK_EN_RSSI_THR      0x00000200
+#define                AR9170_REG_DAG_CTRLCCK_RSSI_THR         0x0001fc00
+#define                AR9170_REG_DAG_CTRLCCK_RSSI_THR_S       10
+
+#define        AR9170_PHY_REG_FORCE_CLKEN_CCK          (AR9170_PHY_REG_BASE + 0x0a2c)
+#define                AR9170_FORCE_CLKEN_CCK_MRC_MUX          0x00000040
+
+#define        AR9170_PHY_REG_POWER_TX_RATE3           (AR9170_PHY_REG_BASE + 0x0a34)
+#define        AR9170_PHY_REG_POWER_TX_RATE4           (AR9170_PHY_REG_BASE + 0x0a38)
+
+#define        AR9170_PHY_REG_SCRM_SEQ_XR              (AR9170_PHY_REG_BASE + 0x0a3c)
+#define        AR9170_PHY_REG_HEADER_DETECT_XR         (AR9170_PHY_REG_BASE + 0x0a40)
+#define        AR9170_PHY_REG_CHIRP_DETECTED_XR        (AR9170_PHY_REG_BASE + 0x0a44)
+#define        AR9170_PHY_REG_BLUETOOTH                (AR9170_PHY_REG_BASE + 0x0a54)
+
+#define        AR9170_PHY_REG_TPCRG1                   (AR9170_PHY_REG_BASE + 0x0a58)
+#define                AR9170_PHY_TPCRG1_NUM_PD_GAIN           0x0000c000
+#define                AR9170_PHY_TPCRG1_NUM_PD_GAIN_S         14
+#define                AR9170_PHY_TPCRG1_PD_GAIN_1             0x00030000
+#define                AR9170_PHY_TPCRG1_PD_GAIN_1_S           16
+#define                AR9170_PHY_TPCRG1_PD_GAIN_2             0x000c0000
+#define                AR9170_PHY_TPCRG1_PD_GAIN_2_S           18
+#define                AR9170_PHY_TPCRG1_PD_GAIN_3             0x00300000
+#define                AR9170_PHY_TPCRG1_PD_GAIN_3_S           20
+#define                AR9170_PHY_TPCRG1_PD_CAL_ENABLE         0x00400000
+#define                AR9170_PHY_TPCRG1_PD_CAL_ENABLE_S       22
+
+#define        AR9170_PHY_REG_TX_PWRCTRL4              (AR9170_PHY_REG_BASE + 0x0a64)
+#define                AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID      0x00000001
+#define                AR9170_PHY_TX_PWRCTRL_PD_AVG_VALID_S    0
+#define                AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT        0x000001fe
+#define                AR9170_PHY_TX_PWRCTRL_PD_AVG_OUT_S      1
+
+#define        AR9170_PHY_REG_ANALOG_SWAP              (AR9170_PHY_REG_BASE + 0x0a68)
+#define                AR9170_PHY_ANALOG_SWAP_AB               0x0001
+#define                AR9170_PHY_ANALOG_SWAP_ALT_CHAIN        0x00000040
+
+#define        AR9170_PHY_REG_TPCRG5                   (AR9170_PHY_REG_BASE + 0x0a6c)
+#define                AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP       0x0000000f
+#define                AR9170_PHY_TPCRG5_PD_GAIN_OVERLAP_S     0
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1    0x000003f0
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S  4
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2    0x0000fc00
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S  10
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3    0x003f0000
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S  16
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4    0x0fc00000
+#define                AR9170_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S  22
+
+#define        AR9170_PHY_REG_TX_PWRCTRL6_0            (AR9170_PHY_REG_BASE + 0x0a70)
+#define        AR9170_PHY_REG_TX_PWRCTRL6_1            (AR9170_PHY_REG_BASE + 0x1a70)
+#define                AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE      0x03000000
+#define                AR9170_PHY_TX_PWRCTRL_ERR_EST_MODE_S    24
+
+#define        AR9170_PHY_REG_TX_PWRCTRL7              (AR9170_PHY_REG_BASE + 0x0a74)
+#define                AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN      0x01f80000
+#define                AR9170_PHY_TX_PWRCTRL_INIT_TX_GAIN_S    19
+
+#define        AR9170_PHY_REG_TX_PWRCTRL9              (AR9170_PHY_REG_BASE + 0x0a7c)
+#define                AR9170_PHY_TX_DESIRED_SCALE_CCK         0x00007c00
+#define                AR9170_PHY_TX_DESIRED_SCALE_CCK_S       10
+#define                AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL   0x80000000
+#define                AR9170_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S 31
+
+#define        AR9170_PHY_REG_TX_GAIN_TBL1             (AR9170_PHY_REG_BASE + 0x0b00)
+#define                AR9170_PHY_TX_GAIN                      0x0007f000
+#define                AR9170_PHY_TX_GAIN_S                    12
+
+/* Carrier leak calibration control, do it after AGC calibration */
+#define        AR9170_PHY_REG_CL_CAL_CTL               (AR9170_PHY_REG_BASE + 0x0b58)
+#define                AR9170_PHY_CL_CAL_ENABLE                0x00000002
+#define                AR9170_PHY_CL_CAL_PARALLEL_CAL_ENABLE   0x00000001
+
+#define        AR9170_PHY_REG_POWER_TX_RATE5           (AR9170_PHY_REG_BASE + 0x0b8c)
+#define        AR9170_PHY_REG_POWER_TX_RATE6           (AR9170_PHY_REG_BASE + 0x0b90)
+
+#define        AR9170_PHY_REG_CH0_TX_PWRCTRL11         (AR9170_PHY_REG_BASE + 0x0b98)
+#define        AR9170_PHY_REG_CH1_TX_PWRCTRL11         (AR9170_PHY_REG_BASE + 0x1b98)
+#define                AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP        0x0000fc00
+#define                AR9170_PHY_TX_CHX_PWRCTRL_OLPC_TEMP_COMP_S      10
+
+#define        AR9170_PHY_REG_CAL_CHAINMASK            (AR9170_PHY_REG_BASE + 0x0b9c)
+#define        AR9170_PHY_REG_VIT_MASK2_M_46_61        (AR9170_PHY_REG_BASE + 0x0ba0)
+#define        AR9170_PHY_REG_MASK2_M_31_45            (AR9170_PHY_REG_BASE + 0x0ba4)
+#define        AR9170_PHY_REG_MASK2_M_16_30            (AR9170_PHY_REG_BASE + 0x0ba8)
+#define        AR9170_PHY_REG_MASK2_M_00_15            (AR9170_PHY_REG_BASE + 0x0bac)
+#define        AR9170_PHY_REG_PILOT_MASK_01_30         (AR9170_PHY_REG_BASE + 0x0bb0)
+#define        AR9170_PHY_REG_PILOT_MASK_31_60         (AR9170_PHY_REG_BASE + 0x0bb4)
+#define        AR9170_PHY_REG_MASK2_P_15_01            (AR9170_PHY_REG_BASE + 0x0bb8)
+#define        AR9170_PHY_REG_MASK2_P_30_16            (AR9170_PHY_REG_BASE + 0x0bbc)
+#define        AR9170_PHY_REG_MASK2_P_45_31            (AR9170_PHY_REG_BASE + 0x0bc0)
+#define        AR9170_PHY_REG_MASK2_P_61_45            (AR9170_PHY_REG_BASE + 0x0bc4)
+#define        AR9170_PHY_REG_POWER_TX_SUB             (AR9170_PHY_REG_BASE + 0x0bc8)
+#define        AR9170_PHY_REG_POWER_TX_RATE7           (AR9170_PHY_REG_BASE + 0x0bcc)
+#define        AR9170_PHY_REG_POWER_TX_RATE8           (AR9170_PHY_REG_BASE + 0x0bd0)
+#define        AR9170_PHY_REG_POWER_TX_RATE9           (AR9170_PHY_REG_BASE + 0x0bd4)
+#define        AR9170_PHY_REG_XPA_CFG                  (AR9170_PHY_REG_BASE + 0x0bd8)
+#define                AR9170_PHY_FORCE_XPA_CFG                0x000000001
+#define                AR9170_PHY_FORCE_XPA_CFG_S              0
+
+#define        AR9170_PHY_REG_CH1_CCA                  (AR9170_PHY_REG_BASE + 0x1064)
+#define                AR9170_PHY_CH1_MINCCA_PWR               0x0ff80000
+#define                AR9170_PHY_CH1_MINCCA_PWR_S             19
+
+#define        AR9170_PHY_REG_CH2_CCA                  (AR9170_PHY_REG_BASE + 0x2064)
+#define                AR9170_PHY_CH2_MINCCA_PWR               0x0ff80000
+#define                AR9170_PHY_CH2_MINCCA_PWR_S             19
+
+#define        AR9170_PHY_REG_CH1_EXT_CCA              (AR9170_PHY_REG_BASE + 0x11bc)
+#define                AR9170_PHY_CH1_EXT_MINCCA_PWR           0xff800000
+#define                AR9170_PHY_CH1_EXT_MINCCA_PWR_S         23
+
+#define        AR9170_PHY_REG_CH2_EXT_CCA              (AR9170_PHY_REG_BASE + 0x21bc)
+#define                AR9170_PHY_CH2_EXT_MINCCA_PWR           0xff800000
+#define                AR9170_PHY_CH2_EXT_MINCCA_PWR_S         23
+
+#define        REDUCE_CHAIN_0 0x00000050
+#define        REDUCE_CHAIN_1 0x00000051
+
+#endif /* __CARL9170_SHARED_PHY_H */
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
new file mode 100644 (file)
index 0000000..671dbc4
--- /dev/null
@@ -0,0 +1,909 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * 802.11 & command trap routines
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
+{
+       bool restart = false;
+       enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON;
+
+       if (len > 3) {
+               if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
+                       ar->fw.err_counter++;
+                       if (ar->fw.err_counter > 3) {
+                               restart = true;
+                               reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
+                       }
+               }
+
+               if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) {
+                       ar->fw.bug_counter++;
+                       restart = true;
+                       reason = CARL9170_RR_FATAL_FIRMWARE_ERROR;
+               }
+       }
+
+       wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf);
+
+       if (restart)
+               carl9170_restart(ar, reason);
+}
+
+static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp)
+{
+       u32 ps;
+       bool new_ps;
+
+       ps = le32_to_cpu(rsp->psm.state);
+
+       new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE;
+       if (ar->ps.state != new_ps) {
+               if (!new_ps) {
+                       ar->ps.sleep_ms = jiffies_to_msecs(jiffies -
+                               ar->ps.last_action);
+               }
+
+               ar->ps.last_action = jiffies;
+
+               ar->ps.state = new_ps;
+       }
+}
+
+static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq)
+{
+       if (ar->cmd_seq < -1)
+               return 0;
+
+       /*
+        * Initialize Counter
+        */
+       if (ar->cmd_seq < 0)
+               ar->cmd_seq = seq;
+
+       /*
+        * The sequence is strictly monotonic increasing and it never skips!
+        *
+        * Therefore we can safely assume that whenever we received an
+        * unexpected sequence we have lost some valuable data.
+        */
+       if (seq != ar->cmd_seq) {
+               int count;
+
+               count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs;
+
+               wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! "
+                         "w:%d g:%d\n", count, ar->cmd_seq, seq);
+
+               carl9170_restart(ar, CARL9170_RR_LOST_RSP);
+               return -EIO;
+       }
+
+       ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs;
+       return 0;
+}
+
+static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer)
+{
+       /*
+        * Some commands may have a variable response length
+        * and we cannot predict the correct length in advance.
+        * So we only check if we provided enough space for the data.
+        */
+       if (unlikely(ar->readlen != (len - 4))) {
+               dev_warn(&ar->udev->dev, "received invalid command response:"
+                        "got %d, instead of %d\n", len - 4, ar->readlen);
+               print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET,
+                       ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f);
+               print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET,
+                       buffer, len);
+               /*
+                * Do not complete. The command times out,
+                * and we get a stack trace from there.
+                */
+               carl9170_restart(ar, CARL9170_RR_INVALID_RSP);
+       }
+
+       spin_lock(&ar->cmd_lock);
+       if (ar->readbuf) {
+               if (len >= 4)
+                       memcpy(ar->readbuf, buffer + 4, len - 4);
+
+               ar->readbuf = NULL;
+       }
+       complete(&ar->cmd_wait);
+       spin_unlock(&ar->cmd_lock);
+}
+
+void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
+{
+       struct carl9170_rsp *cmd = (void *) buf;
+       struct ieee80211_vif *vif;
+
+       if (carl9170_check_sequence(ar, cmd->hdr.seq))
+               return;
+
+       if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) {
+               if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG))
+                       carl9170_cmd_callback(ar, len, buf);
+
+               return;
+       }
+
+       if (unlikely(cmd->hdr.len != (len - 4))) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "FW: received over-/under"
+                               "sized event %x (%d, but should be %d).\n",
+                              cmd->hdr.cmd, cmd->hdr.len, len - 4);
+
+                       print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE,
+                                            buf, len);
+               }
+
+               return;
+       }
+
+       /* hardware event handlers */
+       switch (cmd->hdr.cmd) {
+       case CARL9170_RSP_PRETBTT:
+               /* pre-TBTT event */
+               rcu_read_lock();
+               vif = carl9170_get_main_vif(ar);
+
+               if (!vif) {
+                       rcu_read_unlock();
+                       break;
+               }
+
+               switch (vif->type) {
+               case NL80211_IFTYPE_STATION:
+                       carl9170_handle_ps(ar, cmd);
+                       break;
+
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_ADHOC:
+                       carl9170_update_beacon(ar, true);
+                       break;
+
+               default:
+                       break;
+               }
+               rcu_read_unlock();
+
+               break;
+
+
+       case CARL9170_RSP_TXCOMP:
+               /* TX status notification */
+               carl9170_tx_process_status(ar, cmd);
+               break;
+
+       case CARL9170_RSP_BEACON_CONFIG:
+               /*
+                * (IBSS) beacon send notification
+                * bytes: 04 c2 XX YY B4 B3 B2 B1
+                *
+                * XX always 80
+                * YY always 00
+                * B1-B4 "should" be the number of send out beacons.
+                */
+               break;
+
+       case CARL9170_RSP_ATIM:
+               /* End of Atim Window */
+               break;
+
+       case CARL9170_RSP_WATCHDOG:
+               /* Watchdog Interrupt */
+               carl9170_restart(ar, CARL9170_RR_WATCHDOG);
+               break;
+
+       case CARL9170_RSP_TEXT:
+               /* firmware debug */
+               carl9170_dbg_message(ar, (char *)buf + 4, len - 4);
+               break;
+
+       case CARL9170_RSP_HEXDUMP:
+               wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4);
+               print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE,
+                                    (char *)buf + 4, len - 4);
+               break;
+
+       case CARL9170_RSP_RADAR:
+               if (!net_ratelimit())
+                       break;
+
+               wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
+                      "incident to linux-wireless@vger.kernel.org !\n");
+               break;
+
+       case CARL9170_RSP_GPIO:
+#ifdef CONFIG_CARL9170_WPC
+               if (ar->wps.pbc) {
+                       bool state = !!(cmd->gpio.gpio & cpu_to_le32(
+                               AR9170_GPIO_PORT_WPS_BUTTON_PRESSED));
+
+                       if (state != ar->wps.pbc_state) {
+                               ar->wps.pbc_state = state;
+                               input_report_key(ar->wps.pbc, KEY_WPS_BUTTON,
+                                                state);
+                               input_sync(ar->wps.pbc);
+                       }
+               }
+#endif /* CONFIG_CARL9170_WPC */
+               break;
+
+       case CARL9170_RSP_BOOT:
+               complete(&ar->fw_boot_wait);
+               break;
+
+       default:
+               wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n",
+                       cmd->hdr.cmd);
+               print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
+               break;
+       }
+}
+
+static int carl9170_rx_mac_status(struct ar9170 *ar,
+       struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac,
+       struct ieee80211_rx_status *status)
+{
+       struct ieee80211_channel *chan;
+       u8 error, decrypt;
+
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);
+
+       error = mac->error;
+
+       if (error & AR9170_RX_ERROR_WRONG_RA) {
+               if (!ar->sniffer_enabled)
+                       return -EINVAL;
+       }
+
+       if (error & AR9170_RX_ERROR_PLCP) {
+               if (!(ar->filter_state & FIF_PLCPFAIL))
+                       return -EINVAL;
+
+               status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+       }
+
+       if (error & AR9170_RX_ERROR_FCS) {
+               ar->tx_fcs_errors++;
+
+               if (!(ar->filter_state & FIF_FCSFAIL))
+                       return -EINVAL;
+
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+       }
+
+       decrypt = ar9170_get_decrypt_type(mac);
+       if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+           decrypt != AR9170_ENC_ALG_NONE) {
+               if ((decrypt == AR9170_ENC_ALG_TKIP) &&
+                   (error & AR9170_RX_ERROR_MMIC))
+                       status->flag |= RX_FLAG_MMIC_ERROR;
+
+               status->flag |= RX_FLAG_DECRYPTED;
+       }
+
+       if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled)
+               return -ENODATA;
+
+       error &= ~(AR9170_RX_ERROR_MMIC |
+                  AR9170_RX_ERROR_FCS |
+                  AR9170_RX_ERROR_WRONG_RA |
+                  AR9170_RX_ERROR_DECRYPT |
+                  AR9170_RX_ERROR_PLCP);
+
+       /* drop any other error frames */
+       if (unlikely(error)) {
+               /* TODO: update netdevice's RX dropped/errors statistics */
+
+               if (net_ratelimit())
+                       wiphy_dbg(ar->hw->wiphy, "received frame with "
+                              "suspicious error code (%#x).\n", error);
+
+               return -EINVAL;
+       }
+
+       chan = ar->channel;
+       if (chan) {
+               status->band = chan->band;
+               status->freq = chan->center_freq;
+       }
+
+       switch (mac->status & AR9170_RX_STATUS_MODULATION) {
+       case AR9170_RX_STATUS_MODULATION_CCK:
+               if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+                       status->flag |= RX_FLAG_SHORTPRE;
+               switch (head->plcp[0]) {
+               case AR9170_RX_PHY_RATE_CCK_1M:
+                       status->rate_idx = 0;
+                       break;
+               case AR9170_RX_PHY_RATE_CCK_2M:
+                       status->rate_idx = 1;
+                       break;
+               case AR9170_RX_PHY_RATE_CCK_5M:
+                       status->rate_idx = 2;
+                       break;
+               case AR9170_RX_PHY_RATE_CCK_11M:
+                       status->rate_idx = 3;
+                       break;
+               default:
+                       if (net_ratelimit()) {
+                               wiphy_err(ar->hw->wiphy, "invalid plcp cck "
+                                      "rate (%x).\n", head->plcp[0]);
+                       }
+
+                       return -EINVAL;
+               }
+               break;
+
+       case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+       case AR9170_RX_STATUS_MODULATION_OFDM:
+               switch (head->plcp[0] & 0xf) {
+               case AR9170_TXRX_PHY_RATE_OFDM_6M:
+                       status->rate_idx = 0;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_9M:
+                       status->rate_idx = 1;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_12M:
+                       status->rate_idx = 2;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_18M:
+                       status->rate_idx = 3;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_24M:
+                       status->rate_idx = 4;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_36M:
+                       status->rate_idx = 5;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_48M:
+                       status->rate_idx = 6;
+                       break;
+               case AR9170_TXRX_PHY_RATE_OFDM_54M:
+                       status->rate_idx = 7;
+                       break;
+               default:
+                       if (net_ratelimit()) {
+                               wiphy_err(ar->hw->wiphy, "invalid plcp ofdm "
+                                       "rate (%x).\n", head->plcp[0]);
+                       }
+
+                       return -EINVAL;
+               }
+               if (status->band == IEEE80211_BAND_2GHZ)
+                       status->rate_idx += 4;
+               break;
+
+       case AR9170_RX_STATUS_MODULATION_HT:
+               if (head->plcp[3] & 0x80)
+                       status->flag |= RX_FLAG_40MHZ;
+               if (head->plcp[6] & 0x80)
+                       status->flag |= RX_FLAG_SHORT_GI;
+
+               status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f);
+               status->flag |= RX_FLAG_HT;
+               break;
+
+       default:
+               BUG();
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+
+static void carl9170_rx_phy_status(struct ar9170 *ar,
+       struct ar9170_rx_phystatus *phy, struct ieee80211_rx_status *status)
+{
+       int i;
+
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20);
+
+       for (i = 0; i < 3; i++)
+               if (phy->rssi[i] != 0x80)
+                       status->antenna |= BIT(i);
+
+       /* post-process RSSI */
+       for (i = 0; i < 7; i++)
+               if (phy->rssi[i] & 0x80)
+                       phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f;
+
+       /* TODO: we could do something with phy_errors */
+       status->signal = ar->noise[0] + phy->rssi_combined;
+}
+
+static struct sk_buff *carl9170_rx_copy_data(u8 *buf, int len)
+{
+       struct sk_buff *skb;
+       int reserved = 0;
+       struct ieee80211_hdr *hdr = (void *) buf;
+
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               reserved += NET_IP_ALIGN;
+
+               if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+                       reserved += NET_IP_ALIGN;
+       }
+
+       if (ieee80211_has_a4(hdr->frame_control))
+               reserved += NET_IP_ALIGN;
+
+       reserved = 32 + (reserved & NET_IP_ALIGN);
+
+       skb = dev_alloc_skb(len + reserved);
+       if (likely(skb)) {
+               skb_reserve(skb, reserved);
+               memcpy(skb_put(skb, len), buf, len);
+       }
+
+       return skb;
+}
+
+static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie)
+{
+       struct ieee80211_mgmt *mgmt = (void *)data;
+       u8 *pos, *end;
+
+       pos = (u8 *)mgmt->u.beacon.variable;
+       end = data + len;
+       while (pos < end) {
+               if (pos + 2 + pos[1] > end)
+                       return NULL;
+
+               if (pos[0] == ie)
+                       return pos;
+
+               pos += 2 + pos[1];
+       }
+       return NULL;
+}
+
+/*
+ * NOTE:
+ *
+ * The firmware is in charge of waking up the device just before
+ * the AP is expected to transmit the next beacon.
+ *
+ * This leaves the driver with the important task of deciding when
+ * to set the PHY back to bed again.
+ */
+static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
+{
+       struct ieee80211_hdr *hdr = (void *) data;
+       struct ieee80211_tim_ie *tim_ie;
+       u8 *tim;
+       u8 tim_len;
+       bool cam;
+
+       if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS)))
+               return;
+
+       /* check if this really is a beacon */
+       if (!ieee80211_is_beacon(hdr->frame_control))
+               return;
+
+       /* min. beacon length + FCS_LEN */
+       if (len <= 40 + FCS_LEN)
+               return;
+
+       /* and only beacons from the associated BSSID, please */
+       if (compare_ether_addr(hdr->addr3, ar->common.curbssid) ||
+           !ar->common.curaid)
+               return;
+
+       ar->ps.last_beacon = jiffies;
+
+       tim = carl9170_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
+       if (!tim)
+               return;
+
+       if (tim[1] < sizeof(*tim_ie))
+               return;
+
+       tim_len = tim[1];
+       tim_ie = (struct ieee80211_tim_ie *) &tim[2];
+
+       if (!WARN_ON_ONCE(!ar->hw->conf.ps_dtim_period))
+               ar->ps.dtim_counter = (tim_ie->dtim_count - 1) %
+                       ar->hw->conf.ps_dtim_period;
+
+       /* Check whenever the PHY can be turned off again. */
+
+       /* 1. What about buffered unicast traffic for our AID? */
+       cam = ieee80211_check_tim(tim_ie, tim_len, ar->common.curaid);
+
+       /* 2. Maybe the AP wants to send multicast/broadcast data? */
+       cam = !!(tim_ie->bitmap_ctrl & 0x01);
+
+       if (!cam) {
+               /* back to low-power land. */
+               ar->ps.off_override &= ~PS_OFF_BCN;
+               carl9170_ps_check(ar);
+       } else {
+               /* force CAM */
+               ar->ps.off_override |= PS_OFF_BCN;
+       }
+}
+
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we could
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
+
+static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+       struct ar9170_rx_head *head;
+       struct ar9170_rx_macstatus *mac;
+       struct ar9170_rx_phystatus *phy = NULL;
+       struct ieee80211_rx_status status;
+       struct sk_buff *skb;
+       int mpdu_len;
+
+       if (!IS_STARTED(ar))
+               return;
+
+       if (unlikely(len < sizeof(*mac))) {
+               ar->rx_dropped++;
+               return;
+       }
+
+       mpdu_len = len - sizeof(*mac);
+
+       mac = (void *)(buf + mpdu_len);
+       if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) {
+               ar->rx_dropped++;
+               return;
+       }
+
+       switch (mac->status & AR9170_RX_STATUS_MPDU) {
+       case AR9170_RX_STATUS_MPDU_FIRST:
+               /* Aggregated MPDUs start with an PLCP header */
+               if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) {
+                       head = (void *) buf;
+
+                       /*
+                        * The PLCP header needs to be cached for the
+                        * following MIDDLE + LAST A-MPDU packets.
+                        *
+                        * So, if you are wondering why all frames seem
+                        * to share a common RX status information,
+                        * then you have the answer right here...
+                        */
+                       memcpy(&ar->rx_plcp, (void *) buf,
+                              sizeof(struct ar9170_rx_head));
+
+                       mpdu_len -= sizeof(struct ar9170_rx_head);
+                       buf += sizeof(struct ar9170_rx_head);
+
+                       ar->rx_has_plcp = true;
+               } else {
+                       if (net_ratelimit()) {
+                               wiphy_err(ar->hw->wiphy, "plcp info "
+                                       "is clipped.\n");
+                       }
+
+                       ar->rx_dropped++;
+                       return;
+               }
+               break;
+
+       case AR9170_RX_STATUS_MPDU_LAST:
+               /*
+                * The last frame of an A-MPDU has an extra tail
+                * which does contain the phy status of the whole
+                * aggregate.
+                */
+
+               if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) {
+                       mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+                       phy = (void *)(buf + mpdu_len);
+               } else {
+                       if (net_ratelimit()) {
+                               wiphy_err(ar->hw->wiphy, "frame tail "
+                                       "is clipped.\n");
+                       }
+
+                       ar->rx_dropped++;
+                       return;
+               }
+
+       case AR9170_RX_STATUS_MPDU_MIDDLE:
+               /*  These are just data + mac status */
+               if (unlikely(!ar->rx_has_plcp)) {
+                       if (!net_ratelimit())
+                               return;
+
+                       wiphy_err(ar->hw->wiphy, "rx stream does not start "
+                                       "with a first_mpdu frame tag.\n");
+
+                       ar->rx_dropped++;
+                       return;
+               }
+
+               head = &ar->rx_plcp;
+               break;
+
+       case AR9170_RX_STATUS_MPDU_SINGLE:
+               /* single mpdu has both: plcp (head) and phy status (tail) */
+               head = (void *) buf;
+
+               mpdu_len -= sizeof(struct ar9170_rx_head);
+               mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+
+               buf += sizeof(struct ar9170_rx_head);
+               phy = (void *)(buf + mpdu_len);
+               break;
+
+       default:
+               BUG_ON(1);
+               break;
+       }
+
+       /* FC + DU + RA + FCS */
+       if (unlikely(mpdu_len < (2 + 2 + 6 + FCS_LEN))) {
+               ar->rx_dropped++;
+               return;
+       }
+
+       memset(&status, 0, sizeof(status));
+       if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) {
+               ar->rx_dropped++;
+               return;
+       }
+
+       if (phy)
+               carl9170_rx_phy_status(ar, phy, &status);
+
+       carl9170_ps_beacon(ar, buf, mpdu_len);
+
+       skb = carl9170_rx_copy_data(buf, mpdu_len);
+       if (likely(skb)) {
+               memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
+               ieee80211_rx(ar->hw, skb);
+       } else {
+               ar->rx_dropped++;
+       }
+}
+
+static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf,
+                                  const unsigned int resplen)
+{
+       struct carl9170_rsp *cmd;
+       int i = 0;
+
+       while (i < resplen) {
+               cmd = (void *) &respbuf[i];
+
+               i += cmd->hdr.len + 4;
+               if (unlikely(i > resplen))
+                       break;
+
+               carl9170_handle_command_response(ar, cmd, cmd->hdr.len + 4);
+       }
+
+       if (unlikely(i != resplen)) {
+               if (!net_ratelimit())
+                       return;
+
+               wiphy_err(ar->hw->wiphy, "malformed firmware trap:\n");
+               print_hex_dump_bytes("rxcmd:", DUMP_PREFIX_OFFSET,
+                                    respbuf, resplen);
+       }
+}
+
+static void __carl9170_rx(struct ar9170 *ar, u8 *buf, unsigned int len)
+{
+       unsigned int i = 0;
+
+       /* weird thing, but this is the same in the original driver */
+       while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) {
+               i += 2;
+               len -= 2;
+               buf += 2;
+       }
+
+       if (unlikely(len < 4))
+               return;
+
+       /* found the 6 * 0xffff marker? */
+       if (i == 12)
+               carl9170_rx_untie_cmds(ar, buf, len);
+       else
+               carl9170_handle_mpdu(ar, buf, len);
+}
+
+static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len)
+{
+       unsigned int tlen, wlen = 0, clen = 0;
+       struct ar9170_stream *rx_stream;
+       u8 *tbuf;
+
+       tbuf = buf;
+       tlen = len;
+
+       while (tlen >= 4) {
+               rx_stream = (void *) tbuf;
+               clen = le16_to_cpu(rx_stream->length);
+               wlen = ALIGN(clen, 4);
+
+               /* check if this is stream has a valid tag.*/
+               if (rx_stream->tag != cpu_to_le16(AR9170_RX_STREAM_TAG)) {
+                       /*
+                        * TODO: handle the highly unlikely event that the
+                        * corrupted stream has the TAG at the right position.
+                        */
+
+                       /* check if the frame can be repaired. */
+                       if (!ar->rx_failover_missing) {
+
+                               /* this is not "short read". */
+                               if (net_ratelimit()) {
+                                       wiphy_err(ar->hw->wiphy,
+                                               "missing tag!\n");
+                               }
+
+                               __carl9170_rx(ar, tbuf, tlen);
+                               return;
+                       }
+
+                       if (ar->rx_failover_missing > tlen) {
+                               if (net_ratelimit()) {
+                                       wiphy_err(ar->hw->wiphy,
+                                               "possible multi "
+                                               "stream corruption!\n");
+                                       goto err_telluser;
+                               } else {
+                                       goto err_silent;
+                               }
+                       }
+
+                       memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+                       ar->rx_failover_missing -= tlen;
+
+                       if (ar->rx_failover_missing <= 0) {
+                               /*
+                                * nested carl9170_rx_stream call!
+                                *
+                                * termination is guranteed, even when the
+                                * combined frame also have an element with
+                                * a bad tag.
+                                */
+
+                               ar->rx_failover_missing = 0;
+                               carl9170_rx_stream(ar, ar->rx_failover->data,
+                                                  ar->rx_failover->len);
+
+                               skb_reset_tail_pointer(ar->rx_failover);
+                               skb_trim(ar->rx_failover, 0);
+                       }
+
+                       return;
+               }
+
+               /* check if stream is clipped */
+               if (wlen > tlen - 4) {
+                       if (ar->rx_failover_missing) {
+                               /* TODO: handle double stream corruption. */
+                               if (net_ratelimit()) {
+                                       wiphy_err(ar->hw->wiphy, "double rx "
+                                               "stream corruption!\n");
+                                       goto err_telluser;
+                               } else {
+                                       goto err_silent;
+                               }
+                       }
+
+                       /*
+                        * save incomplete data set.
+                        * the firmware will resend the missing bits when
+                        * the rx - descriptor comes round again.
+                        */
+
+                       memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+                       ar->rx_failover_missing = clen - tlen;
+                       return;
+               }
+               __carl9170_rx(ar, rx_stream->payload, clen);
+
+               tbuf += wlen + 4;
+               tlen -= wlen + 4;
+       }
+
+       if (tlen) {
+               if (net_ratelimit()) {
+                       wiphy_err(ar->hw->wiphy, "%d bytes of unprocessed "
+                               "data left in rx stream!\n", tlen);
+               }
+
+               goto err_telluser;
+       }
+
+       return;
+
+err_telluser:
+       wiphy_err(ar->hw->wiphy, "damaged RX stream data [want:%d, "
+               "data:%d, rx:%d, pending:%d ]\n", clen, wlen, tlen,
+               ar->rx_failover_missing);
+
+       if (ar->rx_failover_missing)
+               print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET,
+                                    ar->rx_failover->data,
+                                    ar->rx_failover->len);
+
+       print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET,
+                            buf, len);
+
+       wiphy_err(ar->hw->wiphy, "please check your hardware and cables, if "
+               "you see this message frequently.\n");
+
+err_silent:
+       if (ar->rx_failover_missing) {
+               skb_reset_tail_pointer(ar->rx_failover);
+               skb_trim(ar->rx_failover, 0);
+               ar->rx_failover_missing = 0;
+       }
+}
+
+void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len)
+{
+       if (ar->fw.rx_stream)
+               carl9170_rx_stream(ar, buf, len);
+       else
+               __carl9170_rx(ar, buf, len);
+}
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
new file mode 100644 (file)
index 0000000..e0d2374
--- /dev/null
@@ -0,0 +1,1373 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * 802.11 xmit & status routines
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static inline unsigned int __carl9170_get_queue(struct ar9170 *ar,
+                                               unsigned int queue)
+{
+       if (unlikely(modparam_noht)) {
+               return queue;
+       } else {
+               /*
+                * This is just another workaround, until
+                * someone figures out how to get QoS and
+                * AMPDU to play nicely together.
+                */
+
+               return 2;               /* AC_BE */
+       }
+}
+
+static inline unsigned int carl9170_get_queue(struct ar9170 *ar,
+                                             struct sk_buff *skb)
+{
+       return __carl9170_get_queue(ar, skb_get_queue_mapping(skb));
+}
+
+static bool is_mem_full(struct ar9170 *ar)
+{
+       return (DIV_ROUND_UP(IEEE80211_MAX_FRAME_LEN, ar->fw.mem_block_size) >
+               atomic_read(&ar->mem_free_blocks));
+}
+
+static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb)
+{
+       int queue, i;
+       bool mem_full;
+
+       atomic_inc(&ar->tx_total_queued);
+
+       queue = skb_get_queue_mapping(skb);
+       spin_lock_bh(&ar->tx_stats_lock);
+
+       /*
+        * The driver has to accept the frame, regardless if the queue is
+        * full to the brim, or not. We have to do the queuing internally,
+        * since mac80211 assumes that a driver which can operate with
+        * aggregated frames does not reject frames for this reason.
+        */
+       ar->tx_stats[queue].len++;
+       ar->tx_stats[queue].count++;
+
+       mem_full = is_mem_full(ar);
+       for (i = 0; i < ar->hw->queues; i++) {
+               if (mem_full || ar->tx_stats[i].len >= ar->tx_stats[i].limit) {
+                       ieee80211_stop_queue(ar->hw, i);
+                       ar->queue_stop_timeout[i] = jiffies;
+               }
+       }
+
+       spin_unlock_bh(&ar->tx_stats_lock);
+}
+
+static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *txinfo;
+       int queue;
+
+       txinfo = IEEE80211_SKB_CB(skb);
+       queue = skb_get_queue_mapping(skb);
+
+       spin_lock_bh(&ar->tx_stats_lock);
+
+       ar->tx_stats[queue].len--;
+
+       if (!is_mem_full(ar)) {
+               unsigned int i;
+               for (i = 0; i < ar->hw->queues; i++) {
+                       if (ar->tx_stats[i].len >= CARL9170_NUM_TX_LIMIT_SOFT)
+                               continue;
+
+                       if (ieee80211_queue_stopped(ar->hw, i)) {
+                               unsigned long tmp;
+
+                               tmp = jiffies - ar->queue_stop_timeout[i];
+                               if (tmp > ar->max_queue_stop_timeout[i])
+                                       ar->max_queue_stop_timeout[i] = tmp;
+                       }
+
+                       ieee80211_wake_queue(ar->hw, i);
+               }
+       }
+
+       spin_unlock_bh(&ar->tx_stats_lock);
+       if (atomic_dec_and_test(&ar->tx_total_queued))
+               complete(&ar->tx_flush);
+}
+
+static int carl9170_alloc_dev_space(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct _carl9170_tx_superframe *super = (void *) skb->data;
+       unsigned int chunks;
+       int cookie = -1;
+
+       atomic_inc(&ar->mem_allocs);
+
+       chunks = DIV_ROUND_UP(skb->len, ar->fw.mem_block_size);
+       if (unlikely(atomic_sub_return(chunks, &ar->mem_free_blocks) < 0)) {
+               atomic_add(chunks, &ar->mem_free_blocks);
+               return -ENOSPC;
+       }
+
+       spin_lock_bh(&ar->mem_lock);
+       cookie = bitmap_find_free_region(ar->mem_bitmap, ar->fw.mem_blocks, 0);
+       spin_unlock_bh(&ar->mem_lock);
+
+       if (unlikely(cookie < 0)) {
+               atomic_add(chunks, &ar->mem_free_blocks);
+               return -ENOSPC;
+       }
+
+       super = (void *) skb->data;
+
+       /*
+        * Cookie #0 serves two special purposes:
+        *  1. The firmware might use it generate BlockACK frames
+        *     in responds of an incoming BlockAckReqs.
+        *
+        *  2. Prevent double-free bugs.
+        */
+       super->s.cookie = (u8) cookie + 1;
+       return 0;
+}
+
+static void carl9170_release_dev_space(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct _carl9170_tx_superframe *super = (void *) skb->data;
+       int cookie;
+
+       /* make a local copy of the cookie */
+       cookie = super->s.cookie;
+       /* invalidate cookie */
+       super->s.cookie = 0;
+
+       /*
+        * Do a out-of-bounds check on the cookie:
+        *
+        *  * cookie "0" is reserved and won't be assigned to any
+        *    out-going frame. Internally however, it is used to
+        *    mark no longer/un-accounted frames and serves as a
+        *    cheap way of preventing frames from being freed
+        *    twice by _accident_. NB: There is a tiny race...
+        *
+        *  * obviously, cookie number is limited by the amount
+        *    of available memory blocks, so the number can
+        *    never execeed the mem_blocks count.
+        */
+       if (unlikely(WARN_ON_ONCE(cookie == 0) ||
+           WARN_ON_ONCE(cookie > ar->fw.mem_blocks)))
+               return;
+
+       atomic_add(DIV_ROUND_UP(skb->len, ar->fw.mem_block_size),
+                  &ar->mem_free_blocks);
+
+       spin_lock_bh(&ar->mem_lock);
+       bitmap_release_region(ar->mem_bitmap, cookie - 1, 0);
+       spin_unlock_bh(&ar->mem_lock);
+}
+
+/* Called from any context */
+static void carl9170_tx_release(struct kref *ref)
+{
+       struct ar9170 *ar;
+       struct carl9170_tx_info *arinfo;
+       struct ieee80211_tx_info *txinfo;
+       struct sk_buff *skb;
+
+       arinfo = container_of(ref, struct carl9170_tx_info, ref);
+       txinfo = container_of((void *) arinfo, struct ieee80211_tx_info,
+                             rate_driver_data);
+       skb = container_of((void *) txinfo, struct sk_buff, cb);
+
+       ar = arinfo->ar;
+       if (WARN_ON_ONCE(!ar))
+               return;
+
+       BUILD_BUG_ON(
+           offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23);
+
+       memset(&txinfo->status.ampdu_ack_len, 0,
+              sizeof(struct ieee80211_tx_info) -
+              offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+
+       if (atomic_read(&ar->tx_total_queued))
+               ar->tx_schedule = true;
+
+       if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) {
+               if (!atomic_read(&ar->tx_ampdu_upload))
+                       ar->tx_ampdu_schedule = true;
+
+               if (txinfo->flags & IEEE80211_TX_STAT_AMPDU) {
+                       txinfo->status.ampdu_len = txinfo->pad[0];
+                       txinfo->status.ampdu_ack_len = txinfo->pad[1];
+                       txinfo->pad[0] = txinfo->pad[1] = 0;
+               } else if (txinfo->flags & IEEE80211_TX_STAT_ACK) {
+                       /*
+                        * drop redundant tx_status reports:
+                        *
+                        * 1. ampdu_ack_len of the final tx_status does
+                        *    include the feedback of this particular frame.
+                        *
+                        * 2. tx_status_irqsafe only queues up to 128
+                        *    tx feedback reports and discards the rest.
+                        *
+                        * 3. minstrel_ht is picky, it only accepts
+                        *    reports of frames with the TX_STATUS_AMPDU flag.
+                        */
+
+                       dev_kfree_skb_any(skb);
+                       return;
+               } else {
+                       /*
+                        * Frame has failed, but we want to keep it in
+                        * case it was lost due to a power-state
+                        * transition.
+                        */
+               }
+       }
+
+       skb_pull(skb, sizeof(struct _carl9170_tx_superframe));
+       ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+
+void carl9170_tx_get_skb(struct sk_buff *skb)
+{
+       struct carl9170_tx_info *arinfo = (void *)
+               (IEEE80211_SKB_CB(skb))->rate_driver_data;
+       kref_get(&arinfo->ref);
+}
+
+int carl9170_tx_put_skb(struct sk_buff *skb)
+{
+       struct carl9170_tx_info *arinfo = (void *)
+               (IEEE80211_SKB_CB(skb))->rate_driver_data;
+
+       return kref_put(&arinfo->ref, carl9170_tx_release);
+}
+
+/* Caller must hold the tid_info->lock & rcu_read_lock */
+static void carl9170_tx_shift_bm(struct ar9170 *ar,
+       struct carl9170_sta_tid *tid_info, u16 seq)
+{
+       u16 off;
+
+       off = SEQ_DIFF(seq, tid_info->bsn);
+
+       if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS))
+               return;
+
+       /*
+        * Sanity check. For each MPDU we set the bit in bitmap and
+        * clear it once we received the tx_status.
+        * But if the bit is already cleared then we've been bitten
+        * by a bug.
+        */
+       WARN_ON_ONCE(!test_and_clear_bit(off, tid_info->bitmap));
+
+       off = SEQ_DIFF(tid_info->snx, tid_info->bsn);
+       if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS))
+               return;
+
+       if (!bitmap_empty(tid_info->bitmap, off))
+               off = find_first_bit(tid_info->bitmap, off);
+
+       tid_info->bsn += off;
+       tid_info->bsn &= 0x0fff;
+
+       bitmap_shift_right(tid_info->bitmap, tid_info->bitmap,
+                          off, CARL9170_BAW_BITS);
+}
+
+static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
+       struct sk_buff *skb, struct ieee80211_tx_info *txinfo)
+{
+       struct _carl9170_tx_superframe *super = (void *) skb->data;
+       struct ieee80211_hdr *hdr = (void *) super->frame_data;
+       struct ieee80211_tx_info *tx_info;
+       struct carl9170_tx_info *ar_info;
+       struct carl9170_sta_info *sta_info;
+       struct ieee80211_sta *sta;
+       struct carl9170_sta_tid *tid_info;
+       struct ieee80211_vif *vif;
+       unsigned int vif_id;
+       u8 tid;
+
+       if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
+           txinfo->flags & IEEE80211_TX_CTL_INJECTED)
+               return;
+
+       tx_info = IEEE80211_SKB_CB(skb);
+       ar_info = (void *) tx_info->rate_driver_data;
+
+       vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
+                CARL9170_TX_SUPER_MISC_VIF_ID_S;
+
+       if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
+               return;
+
+       rcu_read_lock();
+       vif = rcu_dereference(ar->vif_priv[vif_id].vif);
+       if (unlikely(!vif))
+               goto out_rcu;
+
+       /*
+        * Normally we should use wrappers like ieee80211_get_DA to get
+        * the correct peer ieee80211_sta.
+        *
+        * But there is a problem with indirect traffic (broadcasts, or
+        * data which is designated for other stations) in station mode.
+        * The frame will be directed to the AP for distribution and not
+        * to the actual destination.
+        */
+       sta = ieee80211_find_sta(vif, hdr->addr1);
+       if (unlikely(!sta))
+               goto out_rcu;
+
+       tid = get_tid_h(hdr);
+
+       sta_info = (void *) sta->drv_priv;
+       tid_info = rcu_dereference(sta_info->agg[tid]);
+       if (!tid_info)
+               goto out_rcu;
+
+       spin_lock_bh(&tid_info->lock);
+       if (likely(tid_info->state >= CARL9170_TID_STATE_IDLE))
+               carl9170_tx_shift_bm(ar, tid_info, get_seq_h(hdr));
+
+       if (sta_info->stats[tid].clear) {
+               sta_info->stats[tid].clear = false;
+               sta_info->stats[tid].ampdu_len = 0;
+               sta_info->stats[tid].ampdu_ack_len = 0;
+       }
+
+       sta_info->stats[tid].ampdu_len++;
+       if (txinfo->status.rates[0].count == 1)
+               sta_info->stats[tid].ampdu_ack_len++;
+
+       if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) {
+               txinfo->pad[0] = sta_info->stats[tid].ampdu_len;
+               txinfo->pad[1] = sta_info->stats[tid].ampdu_ack_len;
+               txinfo->flags |= IEEE80211_TX_STAT_AMPDU;
+               sta_info->stats[tid].clear = true;
+       }
+       spin_unlock_bh(&tid_info->lock);
+
+out_rcu:
+       rcu_read_unlock();
+}
+
+void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+                       const bool success)
+{
+       struct ieee80211_tx_info *txinfo;
+
+       carl9170_tx_accounting_free(ar, skb);
+
+       txinfo = IEEE80211_SKB_CB(skb);
+
+       if (success)
+               txinfo->flags |= IEEE80211_TX_STAT_ACK;
+       else
+               ar->tx_ack_failures++;
+
+       if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
+               carl9170_tx_status_process_ampdu(ar, skb, txinfo);
+
+       carl9170_tx_put_skb(skb);
+}
+
+/* This function may be called form any context */
+void carl9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+
+       atomic_dec(&ar->tx_total_pending);
+
+       if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
+               atomic_dec(&ar->tx_ampdu_upload);
+
+       if (carl9170_tx_put_skb(skb))
+               tasklet_hi_schedule(&ar->usb_tasklet);
+}
+
+static struct sk_buff *carl9170_get_queued_skb(struct ar9170 *ar, u8 cookie,
+                                              struct sk_buff_head *queue)
+{
+       struct sk_buff *skb;
+
+       spin_lock_bh(&queue->lock);
+       skb_queue_walk(queue, skb) {
+               struct _carl9170_tx_superframe *txc = (void *) skb->data;
+
+               if (txc->s.cookie != cookie)
+                       continue;
+
+               __skb_unlink(skb, queue);
+               spin_unlock_bh(&queue->lock);
+
+               carl9170_release_dev_space(ar, skb);
+               return skb;
+       }
+       spin_unlock_bh(&queue->lock);
+
+       return NULL;
+}
+
+static void carl9170_tx_fill_rateinfo(struct ar9170 *ar, unsigned int rix,
+       unsigned int tries, struct ieee80211_tx_info *txinfo)
+{
+       unsigned int i;
+
+       for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+               if (txinfo->status.rates[i].idx < 0)
+                       break;
+
+               if (i == rix) {
+                       txinfo->status.rates[i].count = tries;
+                       i++;
+                       break;
+               }
+       }
+
+       for (; i < IEEE80211_TX_MAX_RATES; i++) {
+               txinfo->status.rates[i].idx = -1;
+               txinfo->status.rates[i].count = 0;
+       }
+}
+
+static void carl9170_check_queue_stop_timeout(struct ar9170 *ar)
+{
+       int i;
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *txinfo;
+       struct carl9170_tx_info *arinfo;
+       bool restart = false;
+
+       for (i = 0; i < ar->hw->queues; i++) {
+               spin_lock_bh(&ar->tx_status[i].lock);
+
+               skb = skb_peek(&ar->tx_status[i]);
+
+               if (!skb)
+                       goto next;
+
+               txinfo = IEEE80211_SKB_CB(skb);
+               arinfo = (void *) txinfo->rate_driver_data;
+
+               if (time_is_before_jiffies(arinfo->timeout +
+                   msecs_to_jiffies(CARL9170_QUEUE_STUCK_TIMEOUT)) == true)
+                       restart = true;
+
+next:
+               spin_unlock_bh(&ar->tx_status[i].lock);
+       }
+
+       if (restart) {
+               /*
+                * At least one queue has been stuck for long enough.
+                * Give the device a kick and hope it gets back to
+                * work.
+                *
+                * possible reasons may include:
+                *  - frames got lost/corrupted (bad connection to the device)
+                *  - stalled rx processing/usb controller hiccups
+                *  - firmware errors/bugs
+                *  - every bug you can think of.
+                *  - all bugs you can't...
+                *  - ...
+                */
+               carl9170_restart(ar, CARL9170_RR_STUCK_TX);
+       }
+}
+
+void carl9170_tx_janitor(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        tx_janitor.work);
+       if (!IS_STARTED(ar))
+               return;
+
+       ar->tx_janitor_last_run = jiffies;
+
+       carl9170_check_queue_stop_timeout(ar);
+
+       if (!atomic_read(&ar->tx_total_queued))
+               return;
+
+       ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor,
+               msecs_to_jiffies(CARL9170_TX_TIMEOUT));
+}
+
+static void __carl9170_tx_process_status(struct ar9170 *ar,
+       const uint8_t cookie, const uint8_t info)
+{
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *txinfo;
+       struct carl9170_tx_info *arinfo;
+       unsigned int r, t, q;
+       bool success = true;
+
+       q = ar9170_qmap[info & CARL9170_TX_STATUS_QUEUE];
+
+       skb = carl9170_get_queued_skb(ar, cookie, &ar->tx_status[q]);
+       if (!skb) {
+               /*
+                * We have lost the race to another thread.
+                */
+
+               return ;
+       }
+
+       txinfo = IEEE80211_SKB_CB(skb);
+       arinfo = (void *) txinfo->rate_driver_data;
+
+       if (!(info & CARL9170_TX_STATUS_SUCCESS))
+               success = false;
+
+       r = (info & CARL9170_TX_STATUS_RIX) >> CARL9170_TX_STATUS_RIX_S;
+       t = (info & CARL9170_TX_STATUS_TRIES) >> CARL9170_TX_STATUS_TRIES_S;
+
+       carl9170_tx_fill_rateinfo(ar, r, t, txinfo);
+       carl9170_tx_status(ar, skb, success);
+}
+
+void carl9170_tx_process_status(struct ar9170 *ar,
+                               const struct carl9170_rsp *cmd)
+{
+       unsigned int i;
+
+       for (i = 0;  i < cmd->hdr.ext; i++) {
+               if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) {
+                       print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE,
+                                            (void *) cmd, cmd->hdr.len + 4);
+                       break;
+               }
+
+               __carl9170_tx_process_status(ar, cmd->_tx_status[i].cookie,
+                                            cmd->_tx_status[i].info);
+       }
+}
+
+static __le32 carl9170_tx_physet(struct ar9170 *ar,
+       struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate)
+{
+       struct ieee80211_rate *rate = NULL;
+       u32 power, chains;
+       __le32 tmp;
+
+       tmp = cpu_to_le32(0);
+
+       if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ <<
+                       AR9170_TX_PHY_BW_S);
+       /* this works because 40 MHz is 2 and dup is 3 */
+       if (txrate->flags & IEEE80211_TX_RC_DUP_DATA)
+               tmp |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP <<
+                       AR9170_TX_PHY_BW_S);
+
+       if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
+               tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
+
+       if (txrate->flags & IEEE80211_TX_RC_MCS) {
+               u32 r = txrate->idx;
+               u8 *txpower;
+
+               /* heavy clip control */
+               tmp |= cpu_to_le32((r & 0x7) <<
+                       AR9170_TX_PHY_TX_HEAVY_CLIP_S);
+
+               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                       if (info->band == IEEE80211_BAND_5GHZ)
+                               txpower = ar->power_5G_ht40;
+                       else
+                               txpower = ar->power_2G_ht40;
+               } else {
+                       if (info->band == IEEE80211_BAND_5GHZ)
+                               txpower = ar->power_5G_ht20;
+                       else
+                               txpower = ar->power_2G_ht20;
+               }
+
+               power = txpower[r & 7];
+
+               /* +1 dBm for HT40 */
+               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+                       power += 2;
+
+               r <<= AR9170_TX_PHY_MCS_S;
+               BUG_ON(r & ~AR9170_TX_PHY_MCS);
+
+               tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS);
+               tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
+
+               /*
+                * green field preamble does not work.
+                *
+                * if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
+                * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
+                */
+       } else {
+               u8 *txpower;
+               u32 mod;
+               u32 phyrate;
+               u8 idx = txrate->idx;
+
+               if (info->band != IEEE80211_BAND_2GHZ) {
+                       idx += 4;
+                       txpower = ar->power_5G_leg;
+                       mod = AR9170_TX_PHY_MOD_OFDM;
+               } else {
+                       if (idx < 4) {
+                               txpower = ar->power_2G_cck;
+                               mod = AR9170_TX_PHY_MOD_CCK;
+                       } else {
+                               mod = AR9170_TX_PHY_MOD_OFDM;
+                               txpower = ar->power_2G_ofdm;
+                       }
+               }
+
+               rate = &__carl9170_ratetable[idx];
+
+               phyrate = rate->hw_value & 0xF;
+               power = txpower[(rate->hw_value & 0x30) >> 4];
+               phyrate <<= AR9170_TX_PHY_MCS_S;
+
+               tmp |= cpu_to_le32(mod);
+               tmp |= cpu_to_le32(phyrate);
+
+               /*
+                * short preamble seems to be broken too.
+                *
+                * if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+                *      tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
+                */
+       }
+       power <<= AR9170_TX_PHY_TX_PWR_S;
+       power &= AR9170_TX_PHY_TX_PWR;
+       tmp |= cpu_to_le32(power);
+
+       /* set TX chains */
+       if (ar->eeprom.tx_mask == 1) {
+               chains = AR9170_TX_PHY_TXCHAIN_1;
+       } else {
+               chains = AR9170_TX_PHY_TXCHAIN_2;
+
+               /* >= 36M legacy OFDM - use only one chain */
+               if (rate && rate->bitrate >= 360 &&
+                   !(txrate->flags & IEEE80211_TX_RC_MCS))
+                       chains = AR9170_TX_PHY_TXCHAIN_1;
+       }
+       tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S);
+
+       return tmp;
+}
+
+static bool carl9170_tx_rts_check(struct ar9170 *ar,
+                                 struct ieee80211_tx_rate *rate,
+                                 bool ampdu, bool multi)
+{
+       switch (ar->erp_mode) {
+       case CARL9170_ERP_AUTO:
+               if (ampdu)
+                       break;
+
+       case CARL9170_ERP_MAC80211:
+               if (!(rate->flags & IEEE80211_TX_RC_USE_RTS_CTS))
+                       break;
+
+       case CARL9170_ERP_RTS:
+               if (likely(!multi))
+                       return true;
+
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool carl9170_tx_cts_check(struct ar9170 *ar,
+                                 struct ieee80211_tx_rate *rate)
+{
+       switch (ar->erp_mode) {
+       case CARL9170_ERP_AUTO:
+       case CARL9170_ERP_MAC80211:
+               if (!(rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
+                       break;
+
+       case CARL9170_ERP_CTS:
+               return true;
+
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       struct _carl9170_tx_superframe *txc;
+       struct carl9170_vif_info *cvif;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_tx_rate *txrate;
+       struct ieee80211_sta *sta;
+       struct carl9170_tx_info *arinfo;
+       unsigned int hw_queue;
+       int i;
+       u16 keytype = 0;
+       u16 len, icv = 0;
+       bool ampdu, no_ack;
+
+       BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+       BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) !=
+                    CARL9170_TX_SUPERDESC_LEN);
+
+       BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) !=
+                    AR9170_TX_HWDESC_LEN);
+
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
+
+       hw_queue = ar9170_qmap[carl9170_get_queue(ar, skb)];
+
+       hdr = (void *)skb->data;
+       info = IEEE80211_SKB_CB(skb);
+       len = skb->len;
+
+       /*
+        * Note: If the frame was sent through a monitor interface,
+        * the ieee80211_vif pointer can be NULL.
+        */
+       if (likely(info->control.vif))
+               cvif = (void *) info->control.vif->drv_priv;
+       else
+               cvif = NULL;
+
+       sta = info->control.sta;
+
+       txc = (void *)skb_push(skb, sizeof(*txc));
+       memset(txc, 0, sizeof(*txc));
+
+       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
+       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+       if (info->control.hw_key) {
+               icv = info->control.hw_key->icv_len;
+
+               switch (info->control.hw_key->cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+               case WLAN_CIPHER_SUITE_TKIP:
+                       keytype = AR9170_TX_MAC_ENCR_RC4;
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       keytype = AR9170_TX_MAC_ENCR_AES;
+                       break;
+               default:
+                       WARN_ON(1);
+                       goto err_out;
+               }
+       }
+
+       BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC >
+               ((CARL9170_TX_SUPER_MISC_VIF_ID >>
+                CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1));
+
+       txc->s.len = cpu_to_le16(len + sizeof(*txc));
+       txc->f.length = cpu_to_le16(len + icv + 4);
+       SET_VAL(CARL9170_TX_SUPER_MISC_VIF_ID, txc->s.misc,
+               cvif ? cvif->id : 0);
+
+       txc->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION |
+                                        AR9170_TX_MAC_BACKOFF);
+
+       SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txc->s.misc, hw_queue);
+
+       txc->f.mac_control |= cpu_to_le16(hw_queue << AR9170_TX_MAC_QOS_S);
+       txc->f.mac_control |= cpu_to_le16(keytype);
+       txc->f.phy_control = cpu_to_le32(0);
+
+       if (no_ack)
+               txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
+
+       if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
+               txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB;
+
+       txrate = &info->control.rates[0];
+       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+               txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+       else if (carl9170_tx_cts_check(ar, txrate))
+               txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+       SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[0], txrate->count);
+       txc->f.phy_control |= carl9170_tx_physet(ar, info, txrate);
+
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+               for (i = 1; i < CARL9170_TX_MAX_RATES; i++) {
+                       txrate = &info->control.rates[i];
+                       if (txrate->idx >= 0)
+                               continue;
+
+                       txrate->idx = 0;
+                       txrate->count = ar->hw->max_rate_tries;
+               }
+       }
+
+       /*
+        * NOTE: For the first rate, the ERP & AMPDU flags are directly
+        * taken from mac_control. For all fallback rate, the firmware
+        * updates the mac_control flags from the rate info field.
+        */
+       for (i = 1; i < CARL9170_TX_MAX_RATES; i++) {
+               txrate = &info->control.rates[i];
+               if (txrate->idx < 0)
+                       break;
+
+               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
+                       txrate->count);
+
+               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+               else if (carl9170_tx_cts_check(ar, txrate))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+
+               /*
+                * unaggregated fallback, in case aggregation
+                * proves to be unsuccessful and unreliable.
+                */
+               if (ampdu && i < 3)
+                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
+
+               txc->s.rr[i - 1] = carl9170_tx_physet(ar, info, txrate);
+       }
+
+       if (ieee80211_is_probe_resp(hdr->frame_control))
+               txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF;
+
+       if (ampdu) {
+               unsigned int density, factor;
+
+               if (unlikely(!sta || !cvif))
+                       goto err_out;
+
+               density = info->control.sta->ht_cap.ampdu_density;
+               factor = info->control.sta->ht_cap.ampdu_factor;
+
+               if (density) {
+                       /*
+                        * Watch out!
+                        *
+                        * Otus uses slightly different density values than
+                        * those from the 802.11n spec.
+                        */
+
+                       density = max_t(unsigned int, density + 1, 7u);
+               }
+
+               factor = min_t(unsigned int, 1u, factor);
+
+               SET_VAL(CARL9170_TX_SUPER_AMPDU_DENSITY,
+                       txc->s.ampdu_settings, density);
+
+               SET_VAL(CARL9170_TX_SUPER_AMPDU_FACTOR,
+                       txc->s.ampdu_settings, factor);
+
+               if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) {
+                       txc->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+               } else {
+                       /*
+                        * Not sure if it's even possible to aggregate
+                        * non-ht rates with this HW.
+                        */
+                       WARN_ON_ONCE(1);
+               }
+       }
+
+       arinfo = (void *)info->rate_driver_data;
+       arinfo->timeout = jiffies;
+       arinfo->ar = ar;
+       kref_init(&arinfo->ref);
+       return 0;
+
+err_out:
+       skb_pull(skb, sizeof(*txc));
+       return -EINVAL;
+}
+
+static void carl9170_set_immba(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct _carl9170_tx_superframe *super;
+
+       super = (void *) skb->data;
+       super->f.mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_BA);
+}
+
+static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct _carl9170_tx_superframe *super;
+       int tmp;
+
+       super = (void *) skb->data;
+
+       tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_DENSITY) <<
+               CARL9170_TX_SUPER_AMPDU_DENSITY_S;
+
+       /*
+        * If you haven't noticed carl9170_tx_prepare has already filled
+        * in all ampdu spacing & factor parameters.
+        * Now it's the time to check whenever the settings have to be
+        * updated by the firmware, or if everything is still the same.
+        *
+        * There's no sane way to handle different density values with
+        * this hardware, so we may as well just do the compare in the
+        * driver.
+        */
+
+       if (tmp != ar->current_density) {
+               ar->current_density = tmp;
+               super->s.ampdu_settings |=
+                       CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY;
+       }
+
+       tmp = (super->s.ampdu_settings & CARL9170_TX_SUPER_AMPDU_FACTOR) <<
+               CARL9170_TX_SUPER_AMPDU_FACTOR_S;
+
+       if (tmp != ar->current_factor) {
+               ar->current_factor = tmp;
+               super->s.ampdu_settings |=
+                       CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR;
+       }
+}
+
+static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest,
+                                  struct sk_buff *_src)
+{
+       struct _carl9170_tx_superframe *dest, *src;
+
+       dest = (void *) _dest->data;
+       src = (void *) _src->data;
+
+       /*
+        * The mac80211 rate control algorithm expects that all MPDUs in
+        * an AMPDU share the same tx vectors.
+        * This is not really obvious right now, because the hardware
+        * does the AMPDU setup according to its own rulebook.
+        * Our nicely assembled, strictly monotonic increasing mpdu
+        * chains will be broken up, mashed back together...
+        */
+
+       return (dest->f.phy_control == src->f.phy_control);
+}
+
+static void carl9170_tx_ampdu(struct ar9170 *ar)
+{
+       struct sk_buff_head agg;
+       struct carl9170_sta_tid *tid_info;
+       struct sk_buff *skb, *first;
+       unsigned int i = 0, done_ampdus = 0;
+       u16 seq, queue, tmpssn;
+
+       atomic_inc(&ar->tx_ampdu_scheduler);
+       ar->tx_ampdu_schedule = false;
+
+       if (atomic_read(&ar->tx_ampdu_upload))
+               return;
+
+       if (!ar->tx_ampdu_list_len)
+               return;
+
+       __skb_queue_head_init(&agg);
+
+       rcu_read_lock();
+       tid_info = rcu_dereference(ar->tx_ampdu_iter);
+       if (WARN_ON_ONCE(!tid_info)) {
+               rcu_read_unlock();
+               return;
+       }
+
+retry:
+       list_for_each_entry_continue_rcu(tid_info, &ar->tx_ampdu_list, list) {
+               i++;
+
+               if (tid_info->state < CARL9170_TID_STATE_PROGRESS)
+                       continue;
+
+               queue = TID_TO_WME_AC(tid_info->tid);
+
+               spin_lock_bh(&tid_info->lock);
+               if (tid_info->state != CARL9170_TID_STATE_XMIT) {
+                       first = skb_peek(&tid_info->queue);
+                       if (first) {
+                               struct ieee80211_tx_info *txinfo;
+                               struct carl9170_tx_info *arinfo;
+
+                               txinfo = IEEE80211_SKB_CB(first);
+                               arinfo = (void *) txinfo->rate_driver_data;
+
+                               if (time_is_after_jiffies(arinfo->timeout +
+                                   msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))
+                                   == true)
+                                       goto processed;
+
+                               /*
+                                * We've been waiting for the frame which
+                                * matches "snx" (start sequence of the
+                                * next aggregate) for some time now.
+                                *
+                                * But it never arrived. Therefore
+                                * jump to the next available frame
+                                * and kick-start the transmission.
+                                *
+                                * Note: This might induce odd latency
+                                * spikes because the receiver will be
+                                * waiting for the lost frame too.
+                                */
+                               ar->tx_ampdu_timeout++;
+
+                               tid_info->snx = carl9170_get_seq(first);
+                               tid_info->state = CARL9170_TID_STATE_XMIT;
+                       } else {
+                               goto processed;
+                       }
+               }
+
+               tid_info->counter++;
+               first = skb_peek(&tid_info->queue);
+               tmpssn = carl9170_get_seq(first);
+               seq = tid_info->snx;
+
+               if (unlikely(tmpssn != seq)) {
+                       tid_info->state = CARL9170_TID_STATE_IDLE;
+
+                       goto processed;
+               }
+
+               while ((skb = skb_peek(&tid_info->queue))) {
+                       /* strict 0, 1, ..., n - 1, n frame sequence order */
+                       if (unlikely(carl9170_get_seq(skb) != seq))
+                               break;
+
+                       /* don't upload more than AMPDU FACTOR allows. */
+                       if (unlikely(SEQ_DIFF(tid_info->snx, tid_info->bsn) >=
+                           (tid_info->max - 1)))
+                               break;
+
+                       if (!carl9170_tx_rate_check(ar, skb, first))
+                               break;
+
+                       atomic_inc(&ar->tx_ampdu_upload);
+                       tid_info->snx = seq = SEQ_NEXT(seq);
+                       __skb_unlink(skb, &tid_info->queue);
+
+                       __skb_queue_tail(&agg, skb);
+
+                       if (skb_queue_len(&agg) >= CARL9170_NUM_TX_AGG_MAX)
+                               break;
+               }
+
+               if (skb_queue_empty(&tid_info->queue) ||
+                   carl9170_get_seq(skb_peek(&tid_info->queue)) !=
+                   tid_info->snx) {
+                       /*
+                        * stop TID, if A-MPDU frames are still missing,
+                        * or whenever the queue is empty.
+                        */
+
+                       tid_info->state = CARL9170_TID_STATE_IDLE;
+               }
+               done_ampdus++;
+
+processed:
+               spin_unlock_bh(&tid_info->lock);
+
+               if (skb_queue_empty(&agg))
+                       continue;
+
+               /* apply ampdu spacing & factor settings */
+               carl9170_set_ampdu_params(ar, skb_peek(&agg));
+
+               /* set aggregation push bit */
+               carl9170_set_immba(ar, skb_peek_tail(&agg));
+
+               spin_lock_bh(&ar->tx_pending[queue].lock);
+               skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]);
+               spin_unlock_bh(&ar->tx_pending[queue].lock);
+               ar->tx_schedule = true;
+       }
+       if ((done_ampdus++ == 0) && (i++ == 0))
+               goto retry;
+
+       rcu_assign_pointer(ar->tx_ampdu_iter, tid_info);
+       rcu_read_unlock();
+}
+
+static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar,
+                                           struct sk_buff_head *queue)
+{
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
+       struct carl9170_tx_info *arinfo;
+
+       BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+
+       spin_lock_bh(&queue->lock);
+       skb = skb_peek(queue);
+       if (unlikely(!skb))
+               goto err_unlock;
+
+       if (carl9170_alloc_dev_space(ar, skb))
+               goto err_unlock;
+
+       __skb_unlink(skb, queue);
+       spin_unlock_bh(&queue->lock);
+
+       info = IEEE80211_SKB_CB(skb);
+       arinfo = (void *) info->rate_driver_data;
+
+       arinfo->timeout = jiffies;
+
+       /*
+        * increase ref count to "2".
+        * Ref counting is the easiest way to solve the race between
+        * the the urb's completion routine: carl9170_tx_callback and
+        * wlan tx status functions: carl9170_tx_status/janitor.
+        */
+       carl9170_tx_get_skb(skb);
+
+       return skb;
+
+err_unlock:
+       spin_unlock_bh(&queue->lock);
+       return NULL;
+}
+
+void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct _carl9170_tx_superframe *super;
+       uint8_t q = 0;
+
+       ar->tx_dropped++;
+
+       super = (void *)skb->data;
+       SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, q,
+               ar9170_qmap[carl9170_get_queue(ar, skb)]);
+       __carl9170_tx_process_status(ar, super->s.cookie, q);
+}
+
+static void carl9170_tx(struct ar9170 *ar)
+{
+       struct sk_buff *skb;
+       unsigned int i, q;
+       bool schedule_garbagecollector = false;
+
+       ar->tx_schedule = false;
+
+       if (unlikely(!IS_STARTED(ar)))
+               return;
+
+       carl9170_usb_handle_tx_err(ar);
+
+       for (i = 0; i < ar->hw->queues; i++) {
+               while (!skb_queue_empty(&ar->tx_pending[i])) {
+                       skb = carl9170_tx_pick_skb(ar, &ar->tx_pending[i]);
+                       if (unlikely(!skb))
+                               break;
+
+                       atomic_inc(&ar->tx_total_pending);
+
+                       q = __carl9170_get_queue(ar, i);
+                       /*
+                        * NB: tx_status[i] vs. tx_status[q],
+                        * TODO: Move into pick_skb or alloc_dev_space.
+                        */
+                       skb_queue_tail(&ar->tx_status[q], skb);
+
+                       carl9170_usb_tx(ar, skb);
+                       schedule_garbagecollector = true;
+               }
+       }
+
+       if (!schedule_garbagecollector)
+               return;
+
+       ieee80211_queue_delayed_work(ar->hw, &ar->tx_janitor,
+               msecs_to_jiffies(CARL9170_TX_TIMEOUT));
+}
+
+static bool carl9170_tx_ampdu_queue(struct ar9170 *ar,
+       struct ieee80211_sta *sta, struct sk_buff *skb)
+{
+       struct carl9170_sta_info *sta_info;
+       struct carl9170_sta_tid *agg;
+       struct sk_buff *iter;
+       unsigned int max;
+       u16 tid, seq, qseq, off;
+       bool run = false;
+
+       tid = carl9170_get_tid(skb);
+       seq = carl9170_get_seq(skb);
+       sta_info = (void *) sta->drv_priv;
+
+       rcu_read_lock();
+       agg = rcu_dereference(sta_info->agg[tid]);
+       max = sta_info->ampdu_max_len;
+
+       if (!agg)
+               goto err_unlock_rcu;
+
+       spin_lock_bh(&agg->lock);
+       if (unlikely(agg->state < CARL9170_TID_STATE_IDLE))
+               goto err_unlock;
+
+       /* check if sequence is within the BA window */
+       if (unlikely(!BAW_WITHIN(agg->bsn, CARL9170_BAW_BITS, seq)))
+               goto err_unlock;
+
+       if (WARN_ON_ONCE(!BAW_WITHIN(agg->snx, CARL9170_BAW_BITS, seq)))
+               goto err_unlock;
+
+       off = SEQ_DIFF(seq, agg->bsn);
+       if (WARN_ON_ONCE(test_and_set_bit(off, agg->bitmap)))
+               goto err_unlock;
+
+       if (likely(BAW_WITHIN(agg->hsn, CARL9170_BAW_BITS, seq))) {
+               __skb_queue_tail(&agg->queue, skb);
+               agg->hsn = seq;
+               goto queued;
+       }
+
+       skb_queue_reverse_walk(&agg->queue, iter) {
+               qseq = carl9170_get_seq(iter);
+
+               if (BAW_WITHIN(qseq, CARL9170_BAW_BITS, seq)) {
+                       __skb_queue_after(&agg->queue, iter, skb);
+                       goto queued;
+               }
+       }
+
+       __skb_queue_head(&agg->queue, skb);
+queued:
+
+       if (unlikely(agg->state != CARL9170_TID_STATE_XMIT)) {
+               if (agg->snx == carl9170_get_seq(skb_peek(&agg->queue))) {
+                       agg->state = CARL9170_TID_STATE_XMIT;
+                       run = true;
+               }
+       }
+
+       spin_unlock_bh(&agg->lock);
+       rcu_read_unlock();
+
+       return run;
+
+err_unlock:
+       spin_unlock_bh(&agg->lock);
+
+err_unlock_rcu:
+       rcu_read_unlock();
+       carl9170_tx_status(ar, skb, false);
+       ar->tx_dropped++;
+       return false;
+}
+
+int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct ar9170 *ar = hw->priv;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_sta *sta;
+       bool run;
+
+       if (unlikely(!IS_STARTED(ar)))
+               goto err_free;
+
+       info = IEEE80211_SKB_CB(skb);
+       sta = info->control.sta;
+
+       if (unlikely(carl9170_tx_prepare(ar, skb)))
+               goto err_free;
+
+       carl9170_tx_accounting(ar, skb);
+       /*
+        * from now on, one has to use carl9170_tx_status to free
+        * all ressouces which are associated with the frame.
+        */
+
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+               if (WARN_ON_ONCE(!sta))
+                       goto err_free;
+
+               run = carl9170_tx_ampdu_queue(ar, sta, skb);
+               if (run)
+                       carl9170_tx_ampdu(ar);
+
+       } else {
+               unsigned int queue = skb_get_queue_mapping(skb);
+
+               skb_queue_tail(&ar->tx_pending[queue], skb);
+       }
+
+       carl9170_tx(ar);
+       return NETDEV_TX_OK;
+
+err_free:
+       ar->tx_dropped++;
+       dev_kfree_skb_any(skb);
+       return NETDEV_TX_OK;
+}
+
+void carl9170_tx_scheduler(struct ar9170 *ar)
+{
+
+       if (ar->tx_ampdu_schedule)
+               carl9170_tx_ampdu(ar);
+
+       if (ar->tx_schedule)
+               carl9170_tx(ar);
+}
diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c
new file mode 100644 (file)
index 0000000..eb789a9
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ * Atheros CARL9170 driver
+ *
+ * USB - frontend
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/device.h>
+#include <net/mac80211.h>
+#include "carl9170.h"
+#include "cmd.h"
+#include "hw.h"
+#include "fwcmd.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE(CARL9170FW_NAME);
+MODULE_ALIAS("ar9170usb");
+MODULE_ALIAS("arusb_lnx");
+
+/*
+ * Note:
+ *
+ * Always update our wiki's device list (located at:
+ * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ),
+ * whenever you add a new device.
+ */
+static struct usb_device_id carl9170_usb_ids[] = {
+       /* Atheros 9170 */
+       { USB_DEVICE(0x0cf3, 0x9170) },
+       /* Atheros TG121N */
+       { USB_DEVICE(0x0cf3, 0x1001) },
+       /* TP-Link TL-WN821N v2 */
+       { USB_DEVICE(0x0cf3, 0x1002), .driver_info = CARL9170_WPS_BUTTON |
+                CARL9170_ONE_LED },
+       /* 3Com Dual Band 802.11n USB Adapter */
+       { USB_DEVICE(0x0cf3, 0x1010) },
+       /* H3C Dual Band 802.11n USB Adapter */
+       { USB_DEVICE(0x0cf3, 0x1011) },
+       /* Cace Airpcap NX */
+       { USB_DEVICE(0xcace, 0x0300) },
+       /* D-Link DWA 160 A1 */
+       { USB_DEVICE(0x07d1, 0x3c10) },
+       /* D-Link DWA 160 A2 */
+       { USB_DEVICE(0x07d1, 0x3a09) },
+       /* Netgear WNA1000 */
+       { USB_DEVICE(0x0846, 0x9040) },
+       /* Netgear WNDA3100 */
+       { USB_DEVICE(0x0846, 0x9010) },
+       /* Netgear WN111 v2 */
+       { USB_DEVICE(0x0846, 0x9001), .driver_info = CARL9170_ONE_LED },
+       /* Zydas ZD1221 */
+       { USB_DEVICE(0x0ace, 0x1221) },
+       /* Proxim ORiNOCO 802.11n USB */
+       { USB_DEVICE(0x1435, 0x0804) },
+       /* WNC Generic 11n USB Dongle */
+       { USB_DEVICE(0x1435, 0x0326) },
+       /* ZyXEL NWD271N */
+       { USB_DEVICE(0x0586, 0x3417) },
+       /* Z-Com UB81 BG */
+       { USB_DEVICE(0x0cde, 0x0023) },
+       /* Z-Com UB82 ABG */
+       { USB_DEVICE(0x0cde, 0x0026) },
+       /* Sphairon Homelink 1202 */
+       { USB_DEVICE(0x0cde, 0x0027) },
+       /* Arcadyan WN7512 */
+       { USB_DEVICE(0x083a, 0xf522) },
+       /* Planex GWUS300 */
+       { USB_DEVICE(0x2019, 0x5304) },
+       /* IO-Data WNGDNUS2 */
+       { USB_DEVICE(0x04bb, 0x093f) },
+       /* NEC WL300NU-G */
+       { USB_DEVICE(0x0409, 0x0249) },
+       /* AVM FRITZ!WLAN USB Stick N */
+       { USB_DEVICE(0x057c, 0x8401) },
+       /* AVM FRITZ!WLAN USB Stick N 2.4 */
+       { USB_DEVICE(0x057c, 0x8402) },
+       /* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */
+       { USB_DEVICE(0x1668, 0x1200) },
+
+       /* terminate */
+       {}
+};
+MODULE_DEVICE_TABLE(usb, carl9170_usb_ids);
+
+static void carl9170_usb_submit_data_urb(struct ar9170 *ar)
+{
+       struct urb *urb;
+       int err;
+
+       if (atomic_inc_return(&ar->tx_anch_urbs) > AR9170_NUM_TX_URBS)
+               goto err_acc;
+
+       urb = usb_get_from_anchor(&ar->tx_wait);
+       if (!urb)
+               goto err_acc;
+
+       usb_anchor_urb(urb, &ar->tx_anch);
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(err)) {
+               if (net_ratelimit()) {
+                       dev_err(&ar->udev->dev, "tx submit failed (%d)\n",
+                               urb->status);
+               }
+
+               usb_unanchor_urb(urb);
+               usb_anchor_urb(urb, &ar->tx_err);
+       }
+
+       usb_free_urb(urb);
+
+       if (likely(err == 0))
+               return;
+
+err_acc:
+       atomic_dec(&ar->tx_anch_urbs);
+}
+
+static void carl9170_usb_tx_data_complete(struct urb *urb)
+{
+       struct ar9170 *ar = (struct ar9170 *)
+             usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+       if (WARN_ON_ONCE(!ar)) {
+               dev_kfree_skb_irq(urb->context);
+               return;
+       }
+
+       atomic_dec(&ar->tx_anch_urbs);
+
+       switch (urb->status) {
+       /* everything is fine */
+       case 0:
+               carl9170_tx_callback(ar, (void *)urb->context);
+               break;
+
+       /* disconnect */
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               /*
+                * Defer the frame clean-up to the tasklet worker.
+                * This is necessary, because carl9170_tx_drop
+                * does not work in an irqsave context.
+                */
+               usb_anchor_urb(urb, &ar->tx_err);
+               return;
+
+       /* a random transmission error has occurred? */
+       default:
+               if (net_ratelimit()) {
+                       dev_err(&ar->udev->dev, "tx failed (%d)\n",
+                               urb->status);
+               }
+
+               usb_anchor_urb(urb, &ar->tx_err);
+               break;
+       }
+
+       if (likely(IS_STARTED(ar)))
+               carl9170_usb_submit_data_urb(ar);
+}
+
+static int carl9170_usb_submit_cmd_urb(struct ar9170 *ar)
+{
+       struct urb *urb;
+       int err;
+
+       if (atomic_inc_return(&ar->tx_cmd_urbs) != 1) {
+               atomic_dec(&ar->tx_cmd_urbs);
+               return 0;
+       }
+
+       urb = usb_get_from_anchor(&ar->tx_cmd);
+       if (!urb) {
+               atomic_dec(&ar->tx_cmd_urbs);
+               return 0;
+       }
+
+       usb_anchor_urb(urb, &ar->tx_anch);
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(err)) {
+               usb_unanchor_urb(urb);
+               atomic_dec(&ar->tx_cmd_urbs);
+       }
+       usb_free_urb(urb);
+
+       return err;
+}
+
+static void carl9170_usb_cmd_complete(struct urb *urb)
+{
+       struct ar9170 *ar = urb->context;
+       int err = 0;
+
+       if (WARN_ON_ONCE(!ar))
+               return;
+
+       atomic_dec(&ar->tx_cmd_urbs);
+
+       switch (urb->status) {
+       /* everything is fine */
+       case 0:
+               break;
+
+       /* disconnect */
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               return;
+
+       default:
+               err = urb->status;
+               break;
+       }
+
+       if (!IS_INITIALIZED(ar))
+               return;
+
+       if (err)
+               dev_err(&ar->udev->dev, "submit cmd cb failed (%d).\n", err);
+
+       err = carl9170_usb_submit_cmd_urb(ar);
+       if (err)
+               dev_err(&ar->udev->dev, "submit cmd failed (%d).\n", err);
+}
+
+static void carl9170_usb_rx_irq_complete(struct urb *urb)
+{
+       struct ar9170 *ar = urb->context;
+
+       if (WARN_ON_ONCE(!ar))
+               return;
+
+       switch (urb->status) {
+       /* everything is fine */
+       case 0:
+               break;
+
+       /* disconnect */
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               return;
+
+       default:
+               goto resubmit;
+       }
+
+       carl9170_handle_command_response(ar, urb->transfer_buffer,
+                                        urb->actual_length);
+
+resubmit:
+       usb_anchor_urb(urb, &ar->rx_anch);
+       if (unlikely(usb_submit_urb(urb, GFP_ATOMIC)))
+               usb_unanchor_urb(urb);
+}
+
+static int carl9170_usb_submit_rx_urb(struct ar9170 *ar, gfp_t gfp)
+{
+       struct urb *urb;
+       int err = 0, runs = 0;
+
+       while ((atomic_read(&ar->rx_anch_urbs) < AR9170_NUM_RX_URBS) &&
+               (runs++ < AR9170_NUM_RX_URBS)) {
+               err = -ENOSPC;
+               urb = usb_get_from_anchor(&ar->rx_pool);
+               if (urb) {
+                       usb_anchor_urb(urb, &ar->rx_anch);
+                       err = usb_submit_urb(urb, gfp);
+                       if (unlikely(err)) {
+                               usb_unanchor_urb(urb);
+                               usb_anchor_urb(urb, &ar->rx_pool);
+                       } else {
+                               atomic_dec(&ar->rx_pool_urbs);
+                               atomic_inc(&ar->rx_anch_urbs);
+                       }
+                       usb_free_urb(urb);
+               }
+       }
+
+       return err;
+}
+
+static void carl9170_usb_rx_work(struct ar9170 *ar)
+{
+       struct urb *urb;
+       int i;
+
+       for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) {
+               urb = usb_get_from_anchor(&ar->rx_work);
+               if (!urb)
+                       break;
+
+               atomic_dec(&ar->rx_work_urbs);
+               if (IS_INITIALIZED(ar)) {
+                       carl9170_rx(ar, urb->transfer_buffer,
+                                   urb->actual_length);
+               }
+
+               usb_anchor_urb(urb, &ar->rx_pool);
+               atomic_inc(&ar->rx_pool_urbs);
+
+               usb_free_urb(urb);
+
+               carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC);
+       }
+}
+
+void carl9170_usb_handle_tx_err(struct ar9170 *ar)
+{
+       struct urb *urb;
+
+       while ((urb = usb_get_from_anchor(&ar->tx_err))) {
+               struct sk_buff *skb = (void *)urb->context;
+
+               carl9170_tx_drop(ar, skb);
+               carl9170_tx_callback(ar, skb);
+               usb_free_urb(urb);
+       }
+}
+
+static void carl9170_usb_tasklet(unsigned long data)
+{
+       struct ar9170 *ar = (struct ar9170 *) data;
+
+       if (!IS_INITIALIZED(ar))
+               return;
+
+       carl9170_usb_rx_work(ar);
+
+       /*
+        * Strictly speaking: The tx scheduler is not part of the USB system.
+        * But the rx worker returns frames back to the mac80211-stack and
+        * this is the _perfect_ place to generate the next transmissions.
+        */
+       if (IS_STARTED(ar))
+               carl9170_tx_scheduler(ar);
+}
+
+static void carl9170_usb_rx_complete(struct urb *urb)
+{
+       struct ar9170 *ar = (struct ar9170 *)urb->context;
+       int err;
+
+       if (WARN_ON_ONCE(!ar))
+               return;
+
+       atomic_dec(&ar->rx_anch_urbs);
+
+       switch (urb->status) {
+       case 0:
+               /* rx path */
+               usb_anchor_urb(urb, &ar->rx_work);
+               atomic_inc(&ar->rx_work_urbs);
+               break;
+
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               /* handle disconnect events*/
+               return;
+
+       default:
+               /* handle all other errors */
+               usb_anchor_urb(urb, &ar->rx_pool);
+               atomic_inc(&ar->rx_pool_urbs);
+               break;
+       }
+
+       err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC);
+       if (unlikely(err)) {
+               /*
+                * usb_submit_rx_urb reported a problem.
+                * In case this is due to a rx buffer shortage,
+                * elevate the tasklet worker priority to
+                * the highest available level.
+                */
+               tasklet_hi_schedule(&ar->usb_tasklet);
+
+               if (atomic_read(&ar->rx_anch_urbs) == 0) {
+                       /*
+                        * The system is too slow to cope with
+                        * the enormous workload. We have simply
+                        * run out of active rx urbs and this
+                        * unfortunatly leads to an unpredictable
+                        * device.
+                        */
+
+                       carl9170_restart(ar, CARL9170_RR_SLOW_SYSTEM);
+               }
+       } else {
+               /*
+                * Using anything less than _high_ priority absolutely
+                * kills the rx performance my UP-System...
+                */
+               tasklet_hi_schedule(&ar->usb_tasklet);
+       }
+}
+
+static struct urb *carl9170_usb_alloc_rx_urb(struct ar9170 *ar, gfp_t gfp)
+{
+       struct urb *urb;
+       void *buf;
+
+       buf = kmalloc(ar->fw.rx_size, gfp);
+       if (!buf)
+               return NULL;
+
+       urb = usb_alloc_urb(0, gfp);
+       if (!urb) {
+               kfree(buf);
+               return NULL;
+       }
+
+       usb_fill_bulk_urb(urb, ar->udev, usb_rcvbulkpipe(ar->udev,
+                         AR9170_USB_EP_RX), buf, ar->fw.rx_size,
+                         carl9170_usb_rx_complete, ar);
+
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       return urb;
+}
+
+static int carl9170_usb_send_rx_irq_urb(struct ar9170 *ar)
+{
+       struct urb *urb = NULL;
+       void *ibuf;
+       int err = -ENOMEM;
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               goto out;
+
+       ibuf = kmalloc(AR9170_USB_EP_CTRL_MAX, GFP_KERNEL);
+       if (!ibuf)
+               goto out;
+
+       usb_fill_int_urb(urb, ar->udev, usb_rcvintpipe(ar->udev,
+                        AR9170_USB_EP_IRQ), ibuf, AR9170_USB_EP_CTRL_MAX,
+                        carl9170_usb_rx_irq_complete, ar, 1);
+
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       usb_anchor_urb(urb, &ar->rx_anch);
+       err = usb_submit_urb(urb, GFP_KERNEL);
+       if (err)
+               usb_unanchor_urb(urb);
+
+out:
+       usb_free_urb(urb);
+       return err;
+}
+
+static int carl9170_usb_init_rx_bulk_urbs(struct ar9170 *ar)
+{
+       struct urb *urb;
+       int i, err = -EINVAL;
+
+       /*
+        * The driver actively maintains a second shadow
+        * pool for inactive, but fully-prepared rx urbs.
+        *
+        * The pool should help the driver to master huge
+        * workload spikes without running the risk of
+        * undersupplying the hardware or wasting time by
+        * processing rx data (streams) inside the urb
+        * completion (hardirq context).
+        */
+       for (i = 0; i < AR9170_NUM_RX_URBS_POOL; i++) {
+               urb = carl9170_usb_alloc_rx_urb(ar, GFP_KERNEL);
+               if (!urb) {
+                       err = -ENOMEM;
+                       goto err_out;
+               }
+
+               usb_anchor_urb(urb, &ar->rx_pool);
+               atomic_inc(&ar->rx_pool_urbs);
+               usb_free_urb(urb);
+       }
+
+       err = carl9170_usb_submit_rx_urb(ar, GFP_KERNEL);
+       if (err)
+               goto err_out;
+
+       /* the device now waiting for the firmware. */
+       carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE);
+       return 0;
+
+err_out:
+
+       usb_scuttle_anchored_urbs(&ar->rx_pool);
+       usb_scuttle_anchored_urbs(&ar->rx_work);
+       usb_kill_anchored_urbs(&ar->rx_anch);
+       return err;
+}
+
+static int carl9170_usb_flush(struct ar9170 *ar)
+{
+       struct urb *urb;
+       int ret, err = 0;
+
+       while ((urb = usb_get_from_anchor(&ar->tx_wait))) {
+               struct sk_buff *skb = (void *)urb->context;
+               carl9170_tx_drop(ar, skb);
+               carl9170_tx_callback(ar, skb);
+               usb_free_urb(urb);
+       }
+
+       ret = usb_wait_anchor_empty_timeout(&ar->tx_cmd, HZ);
+       if (ret == 0)
+               err = -ETIMEDOUT;
+
+       /* lets wait a while until the tx - queues are dried out */
+       ret = usb_wait_anchor_empty_timeout(&ar->tx_anch, HZ);
+       if (ret == 0)
+               err = -ETIMEDOUT;
+
+       usb_kill_anchored_urbs(&ar->tx_anch);
+       carl9170_usb_handle_tx_err(ar);
+
+       return err;
+}
+
+static void carl9170_usb_cancel_urbs(struct ar9170 *ar)
+{
+       int err;
+
+       carl9170_set_state(ar, CARL9170_UNKNOWN_STATE);
+
+       err = carl9170_usb_flush(ar);
+       if (err)
+               dev_err(&ar->udev->dev, "stuck tx urbs!\n");
+
+       usb_poison_anchored_urbs(&ar->tx_anch);
+       carl9170_usb_handle_tx_err(ar);
+       usb_poison_anchored_urbs(&ar->rx_anch);
+
+       tasklet_kill(&ar->usb_tasklet);
+
+       usb_scuttle_anchored_urbs(&ar->rx_work);
+       usb_scuttle_anchored_urbs(&ar->rx_pool);
+       usb_scuttle_anchored_urbs(&ar->tx_cmd);
+}
+
+int __carl9170_exec_cmd(struct ar9170 *ar, struct carl9170_cmd *cmd,
+                       const bool free_buf)
+{
+       struct urb *urb;
+
+       if (!IS_INITIALIZED(ar))
+               return -EPERM;
+
+       if (WARN_ON(cmd->hdr.len > CARL9170_MAX_CMD_LEN - 4))
+               return -EINVAL;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb)
+               return -ENOMEM;
+
+       usb_fill_int_urb(urb, ar->udev, usb_sndintpipe(ar->udev,
+               AR9170_USB_EP_CMD), cmd, cmd->hdr.len + 4,
+               carl9170_usb_cmd_complete, ar, 1);
+
+       urb->transfer_flags |= URB_ZERO_PACKET;
+
+       if (free_buf)
+               urb->transfer_flags |= URB_FREE_BUFFER;
+
+       usb_anchor_urb(urb, &ar->tx_cmd);
+       usb_free_urb(urb);
+
+       return carl9170_usb_submit_cmd_urb(ar);
+}
+
+int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd,
+       unsigned int plen, void *payload, unsigned int outlen, void *out)
+{
+       int err = -ENOMEM;
+
+       if (!IS_ACCEPTING_CMD(ar))
+               return -EIO;
+
+       if (!(cmd & CARL9170_CMD_ASYNC_FLAG))
+               might_sleep();
+
+       ar->cmd.hdr.len = plen;
+       ar->cmd.hdr.cmd = cmd;
+       /* writing multiple regs fills this buffer already */
+       if (plen && payload != (u8 *)(ar->cmd.data))
+               memcpy(ar->cmd.data, payload, plen);
+
+       spin_lock_bh(&ar->cmd_lock);
+       ar->readbuf = (u8 *)out;
+       ar->readlen = outlen;
+       spin_unlock_bh(&ar->cmd_lock);
+
+       err = __carl9170_exec_cmd(ar, &ar->cmd, false);
+
+       if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) {
+               err = wait_for_completion_timeout(&ar->cmd_wait, HZ);
+               if (err == 0) {
+                       err = -ETIMEDOUT;
+                       goto err_unbuf;
+               }
+
+               if (ar->readlen != outlen) {
+                       err = -EMSGSIZE;
+                       goto err_unbuf;
+               }
+       }
+
+       return 0;
+
+err_unbuf:
+       /* Maybe the device was removed in the moment we were waiting? */
+       if (IS_STARTED(ar)) {
+               dev_err(&ar->udev->dev, "no command feedback "
+                       "received (%d).\n", err);
+
+               /* provide some maybe useful debug information */
+               print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE,
+                                    &ar->cmd, plen + 4);
+
+               carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT);
+       }
+
+       /* invalidate to avoid completing the next command prematurely */
+       spin_lock_bh(&ar->cmd_lock);
+       ar->readbuf = NULL;
+       ar->readlen = 0;
+       spin_unlock_bh(&ar->cmd_lock);
+
+       return err;
+}
+
+void carl9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct urb *urb;
+       struct ar9170_stream *tx_stream;
+       void *data;
+       unsigned int len;
+
+       if (!IS_STARTED(ar))
+               goto err_drop;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb)
+               goto err_drop;
+
+       if (ar->fw.tx_stream) {
+               tx_stream = (void *) (skb->data - sizeof(*tx_stream));
+
+               len = skb->len + sizeof(*tx_stream);
+               tx_stream->length = cpu_to_le16(len);
+               tx_stream->tag = cpu_to_le16(AR9170_TX_STREAM_TAG);
+               data = tx_stream;
+       } else {
+               data = skb->data;
+               len = skb->len;
+       }
+
+       usb_fill_bulk_urb(urb, ar->udev, usb_sndbulkpipe(ar->udev,
+               AR9170_USB_EP_TX), data, len,
+               carl9170_usb_tx_data_complete, skb);
+
+       urb->transfer_flags |= URB_ZERO_PACKET;
+
+       usb_anchor_urb(urb, &ar->tx_wait);
+
+       usb_free_urb(urb);
+
+       carl9170_usb_submit_data_urb(ar);
+       return;
+
+err_drop:
+       carl9170_tx_drop(ar, skb);
+       carl9170_tx_callback(ar, skb);
+}
+
+static void carl9170_release_firmware(struct ar9170 *ar)
+{
+       if (ar->fw.fw) {
+               release_firmware(ar->fw.fw);
+               memset(&ar->fw, 0, sizeof(ar->fw));
+       }
+}
+
+void carl9170_usb_stop(struct ar9170 *ar)
+{
+       int ret;
+
+       carl9170_set_state_when(ar, CARL9170_IDLE, CARL9170_STOPPED);
+
+       ret = carl9170_usb_flush(ar);
+       if (ret)
+               dev_err(&ar->udev->dev, "kill pending tx urbs.\n");
+
+       usb_poison_anchored_urbs(&ar->tx_anch);
+       carl9170_usb_handle_tx_err(ar);
+
+       /* kill any pending command */
+       spin_lock_bh(&ar->cmd_lock);
+       ar->readlen = 0;
+       spin_unlock_bh(&ar->cmd_lock);
+       complete_all(&ar->cmd_wait);
+
+       /* This is required to prevent an early completion on _start */
+       INIT_COMPLETION(ar->cmd_wait);
+
+       /*
+        * Note:
+        * So far we freed all tx urbs, but we won't dare to touch any rx urbs.
+        * Else we would end up with a unresponsive device...
+        */
+}
+
+int carl9170_usb_open(struct ar9170 *ar)
+{
+       usb_unpoison_anchored_urbs(&ar->tx_anch);
+
+       carl9170_set_state_when(ar, CARL9170_STOPPED, CARL9170_IDLE);
+       return 0;
+}
+
+static int carl9170_usb_load_firmware(struct ar9170 *ar)
+{
+       const u8 *data;
+       u8 *buf;
+       unsigned int transfer;
+       size_t len;
+       u32 addr;
+       int err = 0;
+
+       buf = kmalloc(4096, GFP_KERNEL);
+       if (!buf) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       data = ar->fw.fw->data;
+       len = ar->fw.fw->size;
+       addr = ar->fw.address;
+
+       /* this removes the miniboot image */
+       data += ar->fw.offset;
+       len -= ar->fw.offset;
+
+       while (len) {
+               transfer = min_t(unsigned int, len, 4096u);
+               memcpy(buf, data, transfer);
+
+               err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0),
+                                     0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
+                                     addr >> 8, 0, buf, transfer, 100);
+
+               if (err < 0) {
+                       kfree(buf);
+                       goto err_out;
+               }
+
+               len -= transfer;
+               data += transfer;
+               addr += transfer;
+       }
+       kfree(buf);
+
+       err = usb_control_msg(ar->udev, usb_sndctrlpipe(ar->udev, 0),
+                             0x31 /* FW DL COMPLETE */,
+                             0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 200);
+
+       if (wait_for_completion_timeout(&ar->fw_boot_wait, HZ) == 0) {
+               err = -ETIMEDOUT;
+               goto err_out;
+       }
+
+       err = carl9170_echo_test(ar, 0x4a110123);
+       if (err)
+               goto err_out;
+
+       /* firmware restarts cmd counter */
+       ar->cmd_seq = -1;
+
+       return 0;
+
+err_out:
+       dev_err(&ar->udev->dev, "firmware upload failed (%d).\n", err);
+       return err;
+}
+
+int carl9170_usb_restart(struct ar9170 *ar)
+{
+       int err = 0;
+
+       if (ar->intf->condition != USB_INTERFACE_BOUND)
+               return 0;
+
+       /* Disable command response sequence counter. */
+       ar->cmd_seq = -2;
+
+       err = carl9170_reboot(ar);
+
+       carl9170_usb_stop(ar);
+
+       if (err)
+               goto err_out;
+
+       tasklet_schedule(&ar->usb_tasklet);
+
+       /* The reboot procedure can take quite a while to complete. */
+       msleep(1100);
+
+       err = carl9170_usb_open(ar);
+       if (err)
+               goto err_out;
+
+       err = carl9170_usb_load_firmware(ar);
+       if (err)
+               goto err_out;
+
+       return 0;
+
+err_out:
+       carl9170_usb_cancel_urbs(ar);
+       return err;
+}
+
+void carl9170_usb_reset(struct ar9170 *ar)
+{
+       /*
+        * This is the last resort to get the device going again
+        * without any *user replugging action*.
+        *
+        * But there is a catch: usb_reset really is like a physical
+        * *reconnect*. The mac80211 state will be lost in the process.
+        * Therefore a userspace application, which is monitoring
+        * the link must step in.
+        */
+       carl9170_usb_cancel_urbs(ar);
+
+       carl9170_usb_stop(ar);
+
+       usb_queue_reset_device(ar->intf);
+}
+
+static int carl9170_usb_init_device(struct ar9170 *ar)
+{
+       int err;
+
+       err = carl9170_usb_send_rx_irq_urb(ar);
+       if (err)
+               goto err_out;
+
+       err = carl9170_usb_init_rx_bulk_urbs(ar);
+       if (err)
+               goto err_unrx;
+
+       mutex_lock(&ar->mutex);
+       err = carl9170_usb_load_firmware(ar);
+       mutex_unlock(&ar->mutex);
+       if (err)
+               goto err_unrx;
+
+       return 0;
+
+err_unrx:
+       carl9170_usb_cancel_urbs(ar);
+
+err_out:
+       return err;
+}
+
+static void carl9170_usb_firmware_failed(struct ar9170 *ar)
+{
+       struct device *parent = ar->udev->dev.parent;
+       struct usb_device *udev;
+
+       /*
+        * Store a copy of the usb_device pointer locally.
+        * This is because device_release_driver initiates
+        * carl9170_usb_disconnect, which in turn frees our
+        * driver context (ar).
+        */
+       udev = ar->udev;
+
+       complete(&ar->fw_load_wait);
+
+       /* unbind anything failed */
+       if (parent)
+               device_lock(parent);
+
+       device_release_driver(&udev->dev);
+       if (parent)
+               device_unlock(parent);
+
+       usb_put_dev(udev);
+}
+
+static void carl9170_usb_firmware_finish(struct ar9170 *ar)
+{
+       int err;
+
+       err = carl9170_parse_firmware(ar);
+       if (err)
+               goto err_freefw;
+
+       err = carl9170_usb_init_device(ar);
+       if (err)
+               goto err_freefw;
+
+       err = carl9170_usb_open(ar);
+       if (err)
+               goto err_unrx;
+
+       err = carl9170_register(ar);
+
+       carl9170_usb_stop(ar);
+       if (err)
+               goto err_unrx;
+
+       complete(&ar->fw_load_wait);
+       usb_put_dev(ar->udev);
+       return;
+
+err_unrx:
+       carl9170_usb_cancel_urbs(ar);
+
+err_freefw:
+       carl9170_release_firmware(ar);
+       carl9170_usb_firmware_failed(ar);
+}
+
+static void carl9170_usb_firmware_step2(const struct firmware *fw,
+                                       void *context)
+{
+       struct ar9170 *ar = context;
+
+       if (fw) {
+               ar->fw.fw = fw;
+               carl9170_usb_firmware_finish(ar);
+               return;
+       }
+
+       dev_err(&ar->udev->dev, "firmware not found.\n");
+       carl9170_usb_firmware_failed(ar);
+}
+
+static int carl9170_usb_probe(struct usb_interface *intf,
+                             const struct usb_device_id *id)
+{
+       struct ar9170 *ar;
+       struct usb_device *udev;
+       int err;
+
+       err = usb_reset_device(interface_to_usbdev(intf));
+       if (err)
+               return err;
+
+       ar = carl9170_alloc(sizeof(*ar));
+       if (IS_ERR(ar))
+               return PTR_ERR(ar);
+
+       udev = interface_to_usbdev(intf);
+       usb_get_dev(udev);
+       ar->udev = udev;
+       ar->intf = intf;
+       ar->features = id->driver_info;
+
+       usb_set_intfdata(intf, ar);
+       SET_IEEE80211_DEV(ar->hw, &intf->dev);
+
+       init_usb_anchor(&ar->rx_anch);
+       init_usb_anchor(&ar->rx_pool);
+       init_usb_anchor(&ar->rx_work);
+       init_usb_anchor(&ar->tx_wait);
+       init_usb_anchor(&ar->tx_anch);
+       init_usb_anchor(&ar->tx_cmd);
+       init_usb_anchor(&ar->tx_err);
+       init_completion(&ar->cmd_wait);
+       init_completion(&ar->fw_boot_wait);
+       init_completion(&ar->fw_load_wait);
+       tasklet_init(&ar->usb_tasklet, carl9170_usb_tasklet,
+                    (unsigned long)ar);
+
+       atomic_set(&ar->tx_cmd_urbs, 0);
+       atomic_set(&ar->tx_anch_urbs, 0);
+       atomic_set(&ar->rx_work_urbs, 0);
+       atomic_set(&ar->rx_anch_urbs, 0);
+       atomic_set(&ar->rx_pool_urbs, 0);
+       ar->cmd_seq = -2;
+
+       usb_get_dev(ar->udev);
+
+       carl9170_set_state(ar, CARL9170_STOPPED);
+
+       return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
+               &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2);
+}
+
+static void carl9170_usb_disconnect(struct usb_interface *intf)
+{
+       struct ar9170 *ar = usb_get_intfdata(intf);
+       struct usb_device *udev;
+
+       if (WARN_ON(!ar))
+               return;
+
+       udev = ar->udev;
+       wait_for_completion(&ar->fw_load_wait);
+
+       if (IS_INITIALIZED(ar)) {
+               carl9170_reboot(ar);
+               carl9170_usb_stop(ar);
+       }
+
+       carl9170_usb_cancel_urbs(ar);
+       carl9170_unregister(ar);
+
+       usb_set_intfdata(intf, NULL);
+
+       carl9170_release_firmware(ar);
+       carl9170_free(ar);
+       usb_put_dev(udev);
+}
+
+#ifdef CONFIG_PM
+static int carl9170_usb_suspend(struct usb_interface *intf,
+                               pm_message_t message)
+{
+       struct ar9170 *ar = usb_get_intfdata(intf);
+
+       if (!ar)
+               return -ENODEV;
+
+       carl9170_usb_cancel_urbs(ar);
+
+       /*
+        * firmware automatically reboots for usb suspend.
+        */
+
+       return 0;
+}
+
+static int carl9170_usb_resume(struct usb_interface *intf)
+{
+       struct ar9170 *ar = usb_get_intfdata(intf);
+       int err;
+
+       if (!ar)
+               return -ENODEV;
+
+       usb_unpoison_anchored_urbs(&ar->rx_anch);
+
+       err = carl9170_usb_init_device(ar);
+       if (err)
+               goto err_unrx;
+
+       err = carl9170_usb_open(ar);
+       if (err)
+               goto err_unrx;
+
+       return 0;
+
+err_unrx:
+       carl9170_usb_cancel_urbs(ar);
+
+       return err;
+}
+#endif /* CONFIG_PM */
+
+static struct usb_driver carl9170_driver = {
+       .name = KBUILD_MODNAME,
+       .probe = carl9170_usb_probe,
+       .disconnect = carl9170_usb_disconnect,
+       .id_table = carl9170_usb_ids,
+       .soft_unbind = 1,
+#ifdef CONFIG_PM
+       .suspend = carl9170_usb_suspend,
+       .resume = carl9170_usb_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init carl9170_usb_init(void)
+{
+       return usb_register(&carl9170_driver);
+}
+
+static void __exit carl9170_usb_exit(void)
+{
+       usb_deregister(&carl9170_driver);
+}
+
+module_init(carl9170_usb_init);
+module_exit(carl9170_usb_exit);
diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h
new file mode 100644 (file)
index 0000000..0e917f8
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __CARL9170_SHARED_VERSION_H
+#define __CARL9170_SHARED_VERSION_H
+#define CARL9170FW_VERSION_YEAR 10
+#define CARL9170FW_VERSION_MONTH 8
+#define CARL9170FW_VERSION_DAY 30
+#define CARL9170FW_VERSION_GIT "1.8.8.1"
+#endif /* __CARL9170_SHARED_VERSION_H */
diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h
new file mode 100644 (file)
index 0000000..48ead22
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Shared Atheros AR9170 Header
+ *
+ * RX/TX meta descriptor format
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
+ *
+ * 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.
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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 __CARL9170_SHARED_WLAN_H
+#define __CARL9170_SHARED_WLAN_H
+
+#include "fwcmd.h"
+
+#define        AR9170_RX_PHY_RATE_CCK_1M               0x0a
+#define        AR9170_RX_PHY_RATE_CCK_2M               0x14
+#define        AR9170_RX_PHY_RATE_CCK_5M               0x37
+#define        AR9170_RX_PHY_RATE_CCK_11M              0x6e
+
+#define        AR9170_ENC_ALG_NONE                     0x0
+#define        AR9170_ENC_ALG_WEP64                    0x1
+#define        AR9170_ENC_ALG_TKIP                     0x2
+#define        AR9170_ENC_ALG_AESCCMP                  0x4
+#define        AR9170_ENC_ALG_WEP128                   0x5
+#define        AR9170_ENC_ALG_WEP256                   0x6
+#define        AR9170_ENC_ALG_CENC                     0x7
+
+#define        AR9170_RX_ENC_SOFTWARE                  0x8
+
+#define        AR9170_RX_STATUS_MODULATION             0x03
+#define        AR9170_RX_STATUS_MODULATION_S           0
+#define        AR9170_RX_STATUS_MODULATION_CCK         0x00
+#define        AR9170_RX_STATUS_MODULATION_OFDM        0x01
+#define        AR9170_RX_STATUS_MODULATION_HT          0x02
+#define        AR9170_RX_STATUS_MODULATION_DUPOFDM     0x03
+
+/* depends on modulation */
+#define        AR9170_RX_STATUS_SHORT_PREAMBLE         0x08
+#define        AR9170_RX_STATUS_GREENFIELD             0x08
+
+#define        AR9170_RX_STATUS_MPDU                   0x30
+#define        AR9170_RX_STATUS_MPDU_S                 4
+#define        AR9170_RX_STATUS_MPDU_SINGLE            0x00
+#define        AR9170_RX_STATUS_MPDU_FIRST             0x20
+#define        AR9170_RX_STATUS_MPDU_MIDDLE            0x30
+#define        AR9170_RX_STATUS_MPDU_LAST              0x10
+
+#define        AR9170_RX_ERROR_RXTO                    0x01
+#define        AR9170_RX_ERROR_OVERRUN                 0x02
+#define        AR9170_RX_ERROR_DECRYPT                 0x04
+#define        AR9170_RX_ERROR_FCS                     0x08
+#define        AR9170_RX_ERROR_WRONG_RA                0x10
+#define        AR9170_RX_ERROR_PLCP                    0x20
+#define        AR9170_RX_ERROR_MMIC                    0x40
+#define        AR9170_RX_ERROR_FATAL                   0x80
+
+/* these are either-or */
+#define        AR9170_TX_MAC_PROT_RTS                  0x0001
+#define        AR9170_TX_MAC_PROT_CTS                  0x0002
+#define        AR9170_TX_MAC_PROT                      0x0003
+
+#define        AR9170_TX_MAC_NO_ACK                    0x0004
+/* if unset, MAC will only do SIFS space before frame */
+#define        AR9170_TX_MAC_BACKOFF                   0x0008
+#define        AR9170_TX_MAC_BURST                     0x0010
+#define        AR9170_TX_MAC_AGGR                      0x0020
+
+/* encryption is a two-bit field */
+#define        AR9170_TX_MAC_ENCR_NONE                 0x0000
+#define        AR9170_TX_MAC_ENCR_RC4                  0x0040
+#define        AR9170_TX_MAC_ENCR_CENC                 0x0080
+#define        AR9170_TX_MAC_ENCR_AES                  0x00c0
+
+#define        AR9170_TX_MAC_MMIC                      0x0100
+#define        AR9170_TX_MAC_HW_DURATION               0x0200
+#define        AR9170_TX_MAC_QOS_S                     10
+#define        AR9170_TX_MAC_QOS                       0x0c00
+#define        AR9170_TX_MAC_DISABLE_TXOP              0x1000
+#define        AR9170_TX_MAC_TXOP_RIFS                 0x2000
+#define        AR9170_TX_MAC_IMM_BA                    0x4000
+
+/* either-or */
+#define        AR9170_TX_PHY_MOD_CCK                   0x00000000
+#define        AR9170_TX_PHY_MOD_OFDM                  0x00000001
+#define        AR9170_TX_PHY_MOD_HT                    0x00000002
+
+/* depends on modulation */
+#define        AR9170_TX_PHY_SHORT_PREAMBLE            0x00000004
+#define        AR9170_TX_PHY_GREENFIELD                0x00000004
+
+#define        AR9170_TX_PHY_BW_S                      3
+#define        AR9170_TX_PHY_BW                        (3 << AR9170_TX_PHY_BW_SHIFT)
+#define        AR9170_TX_PHY_BW_20MHZ                  0
+#define        AR9170_TX_PHY_BW_40MHZ                  2
+#define        AR9170_TX_PHY_BW_40MHZ_DUP              3
+
+#define        AR9170_TX_PHY_TX_HEAVY_CLIP_S           6
+#define        AR9170_TX_PHY_TX_HEAVY_CLIP             (7 << \
+                                                AR9170_TX_PHY_TX_HEAVY_CLIP_S)
+
+#define        AR9170_TX_PHY_TX_PWR_S                  9
+#define        AR9170_TX_PHY_TX_PWR                    (0x3f << \
+                                                AR9170_TX_PHY_TX_PWR_S)
+
+#define        AR9170_TX_PHY_TXCHAIN_S                 15
+#define        AR9170_TX_PHY_TXCHAIN                   (7 << \
+                                                AR9170_TX_PHY_TXCHAIN_S)
+#define        AR9170_TX_PHY_TXCHAIN_1                 1
+/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
+#define        AR9170_TX_PHY_TXCHAIN_2                 5
+
+#define        AR9170_TX_PHY_MCS_S                     18
+#define        AR9170_TX_PHY_MCS                       (0x7f << \
+                                                AR9170_TX_PHY_MCS_S)
+
+#define        AR9170_TX_PHY_RATE_CCK_1M               0x0
+#define        AR9170_TX_PHY_RATE_CCK_2M               0x1
+#define        AR9170_TX_PHY_RATE_CCK_5M               0x2
+#define        AR9170_TX_PHY_RATE_CCK_11M              0x3
+
+/* same as AR9170_RX_PHY_RATE */
+#define        AR9170_TXRX_PHY_RATE_OFDM_6M            0xb
+#define        AR9170_TXRX_PHY_RATE_OFDM_9M            0xf
+#define        AR9170_TXRX_PHY_RATE_OFDM_12M           0xa
+#define        AR9170_TXRX_PHY_RATE_OFDM_18M           0xe
+#define        AR9170_TXRX_PHY_RATE_OFDM_24M           0x9
+#define        AR9170_TXRX_PHY_RATE_OFDM_36M           0xd
+#define        AR9170_TXRX_PHY_RATE_OFDM_48M           0x8
+#define        AR9170_TXRX_PHY_RATE_OFDM_54M           0xc
+
+#define        AR9170_TXRX_PHY_RATE_HT_MCS0            0x0
+#define        AR9170_TXRX_PHY_RATE_HT_MCS1            0x1
+#define        AR9170_TXRX_PHY_RATE_HT_MCS2            0x2
+#define        AR9170_TXRX_PHY_RATE_HT_MCS3            0x3
+#define        AR9170_TXRX_PHY_RATE_HT_MCS4            0x4
+#define        AR9170_TXRX_PHY_RATE_HT_MCS5            0x5
+#define        AR9170_TXRX_PHY_RATE_HT_MCS6            0x6
+#define        AR9170_TXRX_PHY_RATE_HT_MCS7            0x7
+#define        AR9170_TXRX_PHY_RATE_HT_MCS8            0x8
+#define        AR9170_TXRX_PHY_RATE_HT_MCS9            0x9
+#define        AR9170_TXRX_PHY_RATE_HT_MCS10           0xa
+#define        AR9170_TXRX_PHY_RATE_HT_MCS11           0xb
+#define        AR9170_TXRX_PHY_RATE_HT_MCS12           0xc
+#define        AR9170_TXRX_PHY_RATE_HT_MCS13           0xd
+#define        AR9170_TXRX_PHY_RATE_HT_MCS14           0xe
+#define        AR9170_TXRX_PHY_RATE_HT_MCS15           0xf
+
+#define        AR9170_TX_PHY_SHORT_GI                  0x80000000
+
+#ifdef __CARL9170FW__
+struct ar9170_tx_hw_mac_control {
+       union {
+               struct {
+                       /*
+                        * Beware of compiler bugs in all gcc pre 4.4!
+                        */
+
+                       u8 erp_prot:2;
+                       u8 no_ack:1;
+                       u8 backoff:1;
+                       u8 burst:1;
+                       u8 ampdu:1;
+
+                       u8 enc_mode:2;
+
+                       u8 hw_mmic:1;
+                       u8 hw_duration:1;
+
+                       u8 qos_queue:2;
+
+                       u8 disable_txop:1;
+                       u8 txop_rifs:1;
+
+                       u8 ba_end:1;
+                       u8 probe:1;
+               } __packed;
+
+               __le16 set;
+       } __packed;
+} __packed;
+
+struct ar9170_tx_hw_phy_control {
+       union {
+               struct {
+                       /*
+                        * Beware of compiler bugs in all gcc pre 4.4!
+                        */
+
+                       u8 modulation:2;
+                       u8 preamble:1;
+                       u8 bandwidth:2;
+                       u8:1;
+                       u8 heavy_clip:3;
+                       u8 tx_power:6;
+                       u8 chains:3;
+                       u8 mcs:7;
+                       u8:6;
+                       u8 short_gi:1;
+               } __packed;
+
+               __le32 set;
+       } __packed;
+} __packed;
+
+struct ar9170_tx_rate_info {
+       u8 tries:3;
+       u8 erp_prot:2;
+       u8 ampdu:1;
+       u8 free:2; /* free for use (e.g.:RIFS/TXOP/AMPDU) */
+} __packed;
+
+struct carl9170_tx_superdesc {
+       __le16 len;
+       u8 rix;
+       u8 cnt;
+       u8 cookie;
+       u8 ampdu_density:3;
+       u8 ampdu_factor:2;
+       u8 ampdu_commit_density:1;
+       u8 ampdu_commit_factor:1;
+       u8 ampdu_unused_bit:1;
+       u8 queue:2;
+       u8 reserved:1;
+       u8 vif_id:3;
+       u8 fill_in_tsf:1;
+       u8 cab:1;
+       u8 padding2;
+       struct ar9170_tx_rate_info ri[CARL9170_TX_MAX_RATES];
+       struct ar9170_tx_hw_phy_control rr[CARL9170_TX_MAX_RETRY_RATES];
+} __packed;
+
+struct ar9170_tx_hwdesc {
+       __le16 length;
+       struct ar9170_tx_hw_mac_control mac;
+       struct ar9170_tx_hw_phy_control phy;
+} __packed;
+
+struct ar9170_tx_frame {
+       struct ar9170_tx_hwdesc hdr;
+
+       union {
+               struct ieee80211_hdr i3e;
+               u8 payload[0];
+       } data;
+} __packed;
+
+struct carl9170_tx_superframe {
+       struct carl9170_tx_superdesc s;
+       struct ar9170_tx_frame f;
+} __packed;
+
+#endif /* __CARL9170FW__ */
+
+struct _ar9170_tx_hwdesc {
+       __le16 length;
+       __le16 mac_control;
+       __le32 phy_control;
+} __packed;
+
+#define        CARL9170_TX_SUPER_AMPDU_DENSITY_S               0
+#define        CARL9170_TX_SUPER_AMPDU_DENSITY                 0x7
+#define        CARL9170_TX_SUPER_AMPDU_FACTOR                  0x18
+#define        CARL9170_TX_SUPER_AMPDU_FACTOR_S                3
+#define        CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY          0x20
+#define        CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY_S        5
+#define        CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR           0x40
+#define        CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR_S         6
+
+#define CARL9170_TX_SUPER_MISC_QUEUE                   0x3
+#define CARL9170_TX_SUPER_MISC_QUEUE_S                 0
+#define        CARL9170_TX_SUPER_MISC_VIF_ID                   0x38
+#define        CARL9170_TX_SUPER_MISC_VIF_ID_S                 3
+#define        CARL9170_TX_SUPER_MISC_FILL_IN_TSF              0x40
+#define        CARL9170_TX_SUPER_MISC_CAB                      0x80
+
+#define CARL9170_TX_SUPER_RI_TRIES                     0x7
+#define CARL9170_TX_SUPER_RI_TRIES_S                   0
+#define CARL9170_TX_SUPER_RI_ERP_PROT                  0x18
+#define CARL9170_TX_SUPER_RI_ERP_PROT_S                        3
+#define CARL9170_TX_SUPER_RI_AMPDU                     0x20
+#define CARL9170_TX_SUPER_RI_AMPDU_S                   5
+
+struct _carl9170_tx_superdesc {
+       __le16 len;
+       u8 rix;
+       u8 cnt;
+       u8 cookie;
+       u8 ampdu_settings;
+       u8 misc;
+       u8 padding;
+       u8 ri[CARL9170_TX_MAX_RATES];
+       __le32 rr[CARL9170_TX_MAX_RETRY_RATES];
+} __packed;
+
+struct _carl9170_tx_superframe {
+       struct _carl9170_tx_superdesc s;
+       struct _ar9170_tx_hwdesc f;
+       u8 frame_data[0];
+} __packed;
+
+#define        CARL9170_TX_SUPERDESC_LEN               24
+#define        AR9170_TX_HWDESC_LEN                    8
+#define        AR9170_TX_SUPERFRAME_LEN                (CARL9170_TX_HWDESC_LEN + \
+                                                AR9170_TX_SUPERDESC_LEN)
+
+struct ar9170_rx_head {
+       u8 plcp[12];
+} __packed;
+
+struct ar9170_rx_phystatus {
+       union {
+               struct {
+                       u8 rssi_ant0, rssi_ant1, rssi_ant2,
+                               rssi_ant0x, rssi_ant1x, rssi_ant2x,
+                               rssi_combined;
+               } __packed;
+               u8 rssi[7];
+       } __packed;
+
+       u8 evm_stream0[6], evm_stream1[6];
+       u8 phy_err;
+} __packed;
+
+struct ar9170_rx_macstatus {
+       u8 SAidx, DAidx;
+       u8 error;
+       u8 status;
+} __packed;
+
+struct ar9170_rx_frame_single {
+       struct ar9170_rx_head phy_head;
+       struct ieee80211_hdr i3e;
+       struct ar9170_rx_phystatus phy_tail;
+       struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_head {
+       struct ar9170_rx_head phy_head;
+       struct ieee80211_hdr i3e;
+       struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_middle {
+       struct ieee80211_hdr i3e;
+       struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame_tail {
+       struct ieee80211_hdr i3e;
+       struct ar9170_rx_phystatus phy_tail;
+       struct ar9170_rx_macstatus macstatus;
+} __packed;
+
+struct ar9170_rx_frame {
+       union {
+               struct ar9170_rx_frame_single single;
+               struct ar9170_rx_frame_head head;
+               struct ar9170_rx_frame_middle middle;
+               struct ar9170_rx_frame_tail tail;
+       } __packed;
+} __packed;
+
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
+{
+       return (t->SAidx & 0xc0) >> 4 |
+              (t->DAidx & 0xc0) >> 6;
+}
+
+enum ar9170_txq {
+       AR9170_TXQ_BE,
+
+       AR9170_TXQ_VI,
+       AR9170_TXQ_VO,
+       AR9170_TXQ_BK,
+
+       __AR9170_NUM_TXQ,
+};
+
+static const u8 ar9170_qmap[__AR9170_NUM_TXQ] = { 2, 1, 0, 3 };
+
+#define        AR9170_TXQ_DEPTH                        32
+
+#endif /* __CARL9170_SHARED_WLAN_H */
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
new file mode 100644 (file)
index 0000000..bd21a4d
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ * Copyright (c) 2010 Bruno Randolf <br1@einfach.org>
+ *
+ * Permission to use, copy, modify, and/or 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 <asm/unaligned.h>
+#include <net/mac80211.h>
+
+#include "ath.h"
+#include "reg.h"
+#include "debug.h"
+
+#define REG_READ                       (common->ops->read)
+#define REG_WRITE(_ah, _reg, _val)     (common->ops->write)(_ah, _val, _reg)
+
+#define IEEE80211_WEP_NKID      4       /* number of key ids */
+
+/************************/
+/* Key Cache Management */
+/************************/
+
+bool ath_hw_keyreset(struct ath_common *common, u16 entry)
+{
+       u32 keyType;
+       void *ah = common->ah;
+
+       if (entry >= common->keymax) {
+               ath_print(common, ATH_DBG_FATAL,
+                         "keychache entry %u out of range\n", entry);
+               return false;
+       }
+
+       keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
+
+       REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
+       REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
+       REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
+
+       if (keyType == AR_KEYTABLE_TYPE_TKIP) {
+               u16 micentry = entry + 64;
+
+               REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(ath_hw_keyreset);
+
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac)
+{
+       u32 macHi, macLo;
+       u32 unicast_flag = AR_KEYTABLE_VALID;
+       void *ah = common->ah;
+
+       if (entry >= common->keymax) {
+               ath_print(common, ATH_DBG_FATAL,
+                         "keychache entry %u out of range\n", entry);
+               return false;
+       }
+
+       if (mac != NULL) {
+               /*
+                * AR_KEYTABLE_VALID indicates that the address is a unicast
+                * address, which must match the transmitter address for
+                * decrypting frames.
+                * Not setting this bit allows the hardware to use the key
+                * for multicast frame decryption.
+                */
+               if (mac[0] & 0x01)
+                       unicast_flag = 0;
+
+               macHi = (mac[5] << 8) | mac[4];
+               macLo = (mac[3] << 24) |
+                       (mac[2] << 16) |
+                       (mac[1] << 8) |
+                       mac[0];
+               macLo >>= 1;
+               macLo |= (macHi & 1) << 31;
+               macHi >>= 1;
+       } else {
+               macLo = macHi = 0;
+       }
+       REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
+       REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | unicast_flag);
+
+       return true;
+}
+
+bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
+                                const struct ath_keyval *k,
+                                const u8 *mac)
+{
+       void *ah = common->ah;
+       u32 key0, key1, key2, key3, key4;
+       u32 keyType;
+
+       if (entry >= common->keymax) {
+               ath_print(common, ATH_DBG_FATAL,
+                         "keycache entry %u out of range\n", entry);
+               return false;
+       }
+
+       switch (k->kv_type) {
+       case ATH_CIPHER_AES_OCB:
+               keyType = AR_KEYTABLE_TYPE_AES;
+               break;
+       case ATH_CIPHER_AES_CCM:
+               if (!(common->crypt_caps & ATH_CRYPT_CAP_CIPHER_AESCCM)) {
+                       ath_print(common, ATH_DBG_ANY,
+                                 "AES-CCM not supported by this mac rev\n");
+                       return false;
+               }
+               keyType = AR_KEYTABLE_TYPE_CCM;
+               break;
+       case ATH_CIPHER_TKIP:
+               keyType = AR_KEYTABLE_TYPE_TKIP;
+               if (entry + 64 >= common->keymax) {
+                       ath_print(common, ATH_DBG_ANY,
+                                 "entry %u inappropriate for TKIP\n", entry);
+                       return false;
+               }
+               break;
+       case ATH_CIPHER_WEP:
+               if (k->kv_len < WLAN_KEY_LEN_WEP40) {
+                       ath_print(common, ATH_DBG_ANY,
+                                 "WEP key length %u too small\n", k->kv_len);
+                       return false;
+               }
+               if (k->kv_len <= WLAN_KEY_LEN_WEP40)
+                       keyType = AR_KEYTABLE_TYPE_40;
+               else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
+                       keyType = AR_KEYTABLE_TYPE_104;
+               else
+                       keyType = AR_KEYTABLE_TYPE_128;
+               break;
+       case ATH_CIPHER_CLR:
+               keyType = AR_KEYTABLE_TYPE_CLR;
+               break;
+       default:
+               ath_print(common, ATH_DBG_FATAL,
+                         "cipher %u not supported\n", k->kv_type);
+               return false;
+       }
+
+       key0 = get_unaligned_le32(k->kv_val + 0);
+       key1 = get_unaligned_le16(k->kv_val + 4);
+       key2 = get_unaligned_le32(k->kv_val + 6);
+       key3 = get_unaligned_le16(k->kv_val + 10);
+       key4 = get_unaligned_le32(k->kv_val + 12);
+       if (k->kv_len <= WLAN_KEY_LEN_WEP104)
+               key4 &= 0xff;
+
+       /*
+        * Note: Key cache registers access special memory area that requires
+        * two 32-bit writes to actually update the values in the internal
+        * memory. Consequently, the exact order and pairs used here must be
+        * maintained.
+        */
+
+       if (keyType == AR_KEYTABLE_TYPE_TKIP) {
+               u16 micentry = entry + 64;
+
+               /*
+                * Write inverted key[47:0] first to avoid Michael MIC errors
+                * on frames that could be sent or received at the same time.
+                * The correct key will be written in the end once everything
+                * else is ready.
+                */
+               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
+
+               /* Write key[95:48] */
+               REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+               REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+
+               /* Write key[127:96] and key type */
+               REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+               REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+
+               /* Write MAC address for the entry */
+               (void) ath_hw_keysetmac(common, entry, mac);
+
+               if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) {
+                       /*
+                        * TKIP uses two key cache entries:
+                        * Michael MIC TX/RX keys in the same key cache entry
+                        * (idx = main index + 64):
+                        * key0 [31:0] = RX key [31:0]
+                        * key1 [15:0] = TX key [31:16]
+                        * key1 [31:16] = reserved
+                        * key2 [31:0] = RX key [63:32]
+                        * key3 [15:0] = TX key [15:0]
+                        * key3 [31:16] = reserved
+                        * key4 [31:0] = TX key [63:32]
+                        */
+                       u32 mic0, mic1, mic2, mic3, mic4;
+
+                       mic0 = get_unaligned_le32(k->kv_mic + 0);
+                       mic2 = get_unaligned_le32(k->kv_mic + 4);
+                       mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
+                       mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
+                       mic4 = get_unaligned_le32(k->kv_txmic + 4);
+
+                       /* Write RX[31:0] and TX[31:16] */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+                       REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
+
+                       /* Write RX[63:32] and TX[15:0] */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+                       REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
+
+                       /* Write TX[63:32] and keyType(reserved) */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
+                       REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+                                 AR_KEYTABLE_TYPE_CLR);
+
+               } else {
+                       /*
+                        * TKIP uses four key cache entries (two for group
+                        * keys):
+                        * Michael MIC TX/RX keys are in different key cache
+                        * entries (idx = main index + 64 for TX and
+                        * main index + 32 + 96 for RX):
+                        * key0 [31:0] = TX/RX MIC key [31:0]
+                        * key1 [31:0] = reserved
+                        * key2 [31:0] = TX/RX MIC key [63:32]
+                        * key3 [31:0] = reserved
+                        * key4 [31:0] = reserved
+                        *
+                        * Upper layer code will call this function separately
+                        * for TX and RX keys when these registers offsets are
+                        * used.
+                        */
+                       u32 mic0, mic2;
+
+                       mic0 = get_unaligned_le32(k->kv_mic + 0);
+                       mic2 = get_unaligned_le32(k->kv_mic + 4);
+
+                       /* Write MIC key[31:0] */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+                       REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+
+                       /* Write MIC key[63:32] */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+                       REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+
+                       /* Write TX[63:32] and keyType(reserved) */
+                       REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
+                       REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+                                 AR_KEYTABLE_TYPE_CLR);
+               }
+
+               /* MAC address registers are reserved for the MIC entry */
+               REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
+               REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
+
+               /*
+                * Write the correct (un-inverted) key[47:0] last to enable
+                * TKIP now that all other registers are set with correct
+                * values.
+                */
+               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+       } else {
+               /* Write key[47:0] */
+               REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+               REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+
+               /* Write key[95:48] */
+               REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+               REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+
+               /* Write key[127:96] and key type */
+               REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+               REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+
+               /* Write MAC address for the entry */
+               (void) ath_hw_keysetmac(common, entry, mac);
+       }
+
+       return true;
+}
+
+static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
+                          struct ath_keyval *hk, const u8 *addr,
+                          bool authenticator)
+{
+       const u8 *key_rxmic;
+       const u8 *key_txmic;
+
+       key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
+       key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+
+       if (addr == NULL) {
+               /*
+                * Group key installation - only two key cache entries are used
+                * regardless of splitmic capability since group key is only
+                * used either for TX or RX.
+                */
+               if (authenticator) {
+                       memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+                       memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
+               } else {
+                       memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+                       memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
+               }
+               return ath_hw_set_keycache_entry(common, keyix, hk, addr);
+       }
+       if (common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) {
+               /* TX and RX keys share the same key cache entry. */
+               memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+               memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
+               return ath_hw_set_keycache_entry(common, keyix, hk, addr);
+       }
+
+       /* Separate key cache entries for TX and RX */
+
+       /* TX key goes at first index, RX key at +32. */
+       memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
+       if (!ath_hw_set_keycache_entry(common, keyix, hk, NULL)) {
+               /* TX MIC entry failed. No need to proceed further */
+               ath_print(common, ATH_DBG_FATAL,
+                         "Setting TX MIC Key Failed\n");
+               return 0;
+       }
+
+       memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+       /* XXX delete tx key on failure? */
+       return ath_hw_set_keycache_entry(common, keyix + 32, hk, addr);
+}
+
+static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
+{
+       int i;
+
+       for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+               if (test_bit(i, common->keymap) ||
+                   test_bit(i + 64, common->keymap))
+                       continue; /* At least one part of TKIP key allocated */
+               if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED) &&
+                   (test_bit(i + 32, common->keymap) ||
+                    test_bit(i + 64 + 32, common->keymap)))
+                       continue; /* At least one part of TKIP key allocated */
+
+               /* Found a free slot for a TKIP key */
+               return i;
+       }
+       return -1;
+}
+
+static int ath_reserve_key_cache_slot(struct ath_common *common,
+                                     u32 cipher)
+{
+       int i;
+
+       if (cipher == WLAN_CIPHER_SUITE_TKIP)
+               return ath_reserve_key_cache_slot_tkip(common);
+
+       /* First, try to find slots that would not be available for TKIP. */
+       if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+               for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
+                       if (!test_bit(i, common->keymap) &&
+                           (test_bit(i + 32, common->keymap) ||
+                            test_bit(i + 64, common->keymap) ||
+                            test_bit(i + 64 + 32, common->keymap)))
+                               return i;
+                       if (!test_bit(i + 32, common->keymap) &&
+                           (test_bit(i, common->keymap) ||
+                            test_bit(i + 64, common->keymap) ||
+                            test_bit(i + 64 + 32, common->keymap)))
+                               return i + 32;
+                       if (!test_bit(i + 64, common->keymap) &&
+                           (test_bit(i , common->keymap) ||
+                            test_bit(i + 32, common->keymap) ||
+                            test_bit(i + 64 + 32, common->keymap)))
+                               return i + 64;
+                       if (!test_bit(i + 64 + 32, common->keymap) &&
+                           (test_bit(i, common->keymap) ||
+                            test_bit(i + 32, common->keymap) ||
+                            test_bit(i + 64, common->keymap)))
+                               return i + 64 + 32;
+               }
+       } else {
+               for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+                       if (!test_bit(i, common->keymap) &&
+                           test_bit(i + 64, common->keymap))
+                               return i;
+                       if (test_bit(i, common->keymap) &&
+                           !test_bit(i + 64, common->keymap))
+                               return i + 64;
+               }
+       }
+
+       /* No partially used TKIP slots, pick any available slot */
+       for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
+               /* Do not allow slots that could be needed for TKIP group keys
+                * to be used. This limitation could be removed if we know that
+                * TKIP will not be used. */
+               if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
+                       continue;
+               if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+                       if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
+                               continue;
+                       if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
+                               continue;
+               }
+
+               if (!test_bit(i, common->keymap))
+                       return i; /* Found a free slot for a key */
+       }
+
+       /* No free slot found */
+       return -1;
+}
+
+/*
+ * Configure encryption in the HW.
+ */
+int ath_key_config(struct ath_common *common,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct ath_keyval hk;
+       const u8 *mac = NULL;
+       u8 gmac[ETH_ALEN];
+       int ret = 0;
+       int idx;
+
+       memset(&hk, 0, sizeof(hk));
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               hk.kv_type = ATH_CIPHER_WEP;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               hk.kv_type = ATH_CIPHER_TKIP;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               hk.kv_type = ATH_CIPHER_AES_CCM;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       hk.kv_len = key->keylen;
+       memcpy(hk.kv_val, key->key, key->keylen);
+
+       if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+               switch (vif->type) {
+               case NL80211_IFTYPE_AP:
+                       memcpy(gmac, vif->addr, ETH_ALEN);
+                       gmac[0] |= 0x01;
+                       mac = gmac;
+                       idx = ath_reserve_key_cache_slot(common, key->cipher);
+                       break;
+               case NL80211_IFTYPE_ADHOC:
+                       if (!sta) {
+                               idx = key->keyidx;
+                               break;
+                       }
+                       memcpy(gmac, sta->addr, ETH_ALEN);
+                       gmac[0] |= 0x01;
+                       mac = gmac;
+                       idx = ath_reserve_key_cache_slot(common, key->cipher);
+                       break;
+               default:
+                       idx = key->keyidx;
+                       break;
+               }
+       } else if (key->keyidx) {
+               if (WARN_ON(!sta))
+                       return -EOPNOTSUPP;
+               mac = sta->addr;
+
+               if (vif->type != NL80211_IFTYPE_AP) {
+                       /* Only keyidx 0 should be used with unicast key, but
+                        * allow this for client mode for now. */
+                       idx = key->keyidx;
+               } else
+                       return -EIO;
+       } else {
+               if (WARN_ON(!sta))
+                       return -EOPNOTSUPP;
+               mac = sta->addr;
+
+               idx = ath_reserve_key_cache_slot(common, key->cipher);
+       }
+
+       if (idx < 0)
+               return -ENOSPC; /* no free key cache entries */
+
+       if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+               ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
+                                     vif->type == NL80211_IFTYPE_AP);
+       else
+               ret = ath_hw_set_keycache_entry(common, idx, &hk, mac);
+
+       if (!ret)
+               return -EIO;
+
+       set_bit(idx, common->keymap);
+       if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+               set_bit(idx + 64, common->keymap);
+               set_bit(idx, common->tkip_keymap);
+               set_bit(idx + 64, common->tkip_keymap);
+               if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+                       set_bit(idx + 32, common->keymap);
+                       set_bit(idx + 64 + 32, common->keymap);
+                       set_bit(idx + 32, common->tkip_keymap);
+                       set_bit(idx + 64 + 32, common->tkip_keymap);
+               }
+       }
+
+       return idx;
+}
+EXPORT_SYMBOL(ath_key_config);
+
+/*
+ * Delete Key.
+ */
+void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
+{
+       ath_hw_keyreset(common, key->hw_key_idx);
+       if (key->hw_key_idx < IEEE80211_WEP_NKID)
+               return;
+
+       clear_bit(key->hw_key_idx, common->keymap);
+       if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
+               return;
+
+       clear_bit(key->hw_key_idx + 64, common->keymap);
+
+       clear_bit(key->hw_key_idx, common->tkip_keymap);
+       clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
+
+       if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
+               ath_hw_keyreset(common, key->hw_key_idx + 32);
+               clear_bit(key->hw_key_idx + 32, common->keymap);
+               clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
+
+               clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
+               clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
+       }
+}
+EXPORT_SYMBOL(ath_key_delete);
index dfe1fbec24f5fbfa3dade5ac2a9e040b45143348..e798ef476581a7c7ac508416659aa7b544f7dec5 100644 (file)
 #define AR_BSSMSKL             0x80e0
 #define AR_BSSMSKU             0x80e4
 
+#define AR_KEYTABLE_0           0x8800
+#define AR_KEYTABLE(_n)         (AR_KEYTABLE_0 + ((_n)*32))
+#define AR_KEY_CACHE_SIZE       128
+#define AR_RSVD_KEYTABLE_ENTRIES 4
+#define AR_KEY_TYPE             0x00000007
+#define AR_KEYTABLE_TYPE_40     0x00000000
+#define AR_KEYTABLE_TYPE_104    0x00000001
+#define AR_KEYTABLE_TYPE_128    0x00000003
+#define AR_KEYTABLE_TYPE_TKIP   0x00000004
+#define AR_KEYTABLE_TYPE_AES    0x00000005
+#define AR_KEYTABLE_TYPE_CCM    0x00000006
+#define AR_KEYTABLE_TYPE_CLR    0x00000007
+#define AR_KEYTABLE_ANT         0x00000008
+#define AR_KEYTABLE_VALID       0x00008000
+#define AR_KEYTABLE_KEY0(_n)    (AR_KEYTABLE(_n) + 0)
+#define AR_KEYTABLE_KEY1(_n)    (AR_KEYTABLE(_n) + 4)
+#define AR_KEYTABLE_KEY2(_n)    (AR_KEYTABLE(_n) + 8)
+#define AR_KEYTABLE_KEY3(_n)    (AR_KEYTABLE(_n) + 12)
+#define AR_KEYTABLE_KEY4(_n)    (AR_KEYTABLE(_n) + 16)
+#define AR_KEYTABLE_TYPE(_n)    (AR_KEYTABLE(_n) + 20)
+#define AR_KEYTABLE_MAC0(_n)    (AR_KEYTABLE(_n) + 24)
+#define AR_KEYTABLE_MAC1(_n)    (AR_KEYTABLE(_n) + 28)
+
 #endif /* ATH_REGISTERS_H */
index 674fb93ae17f0829d02906173326f7b59e98e15c..56ef4ed0db476a7c6560d5b4d7f297e21cfbb5ac 100644 (file)
@@ -223,6 +223,7 @@ static struct iwl_lib_ops iwl1000_lib = {
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
                .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
index bb2aeebf3652c1ce38bb8620f822a64e3f2f4782..98509c5e708d20bf30b0ac27758e3b979822c1e3 100644 (file)
@@ -295,7 +295,7 @@ extern const struct iwl_channel_info *iwl3945_get_channel_info(
 extern int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate);
 
 /* scanning */
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* Requires full declaration of iwl_priv before including */
 #include "iwl-io.h"
index 1d6a46d4db594dbf9fe8ae34f4dc702ebb05063f..943a9c7bfa7fdeeb71fce24dba5cee3734a68946 100644 (file)
@@ -2289,6 +2289,7 @@ static struct iwl_lib_ops iwl4965_lib = {
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
                .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
index 1dbb1246c083e98ed54f8d5f016794f74b5ee1be..21b4b23368e6e7129820f3d55427cee01059c534 100644 (file)
@@ -404,6 +404,7 @@ static struct iwl_lib_ops iwl5000_lib = {
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
                .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
@@ -474,6 +475,8 @@ static struct iwl_lib_ops iwl5150_lib = {
                .rx_stats_read = iwl_ucode_rx_stats_read,
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
+               .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
index 2fdba088bd276d5c3ef5fb7f31b6944a0be4e911..9f43f2770c96897e39fed5118abdf311af5731bb 100644 (file)
@@ -329,6 +329,7 @@ static struct iwl_lib_ops iwl6000_lib = {
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
                .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
@@ -404,6 +405,7 @@ static struct iwl_lib_ops iwl6000g2b_lib = {
                .tx_stats_read = iwl_ucode_tx_stats_read,
                .general_stats_read = iwl_ucode_general_stats_read,
                .bt_stats_read = iwl_ucode_bt_stats_read,
+               .reply_tx_error = iwl_reply_tx_error_read,
        },
        .recover_from_tx_stall = iwl_bg_monitor_recover,
        .check_plcp_health = iwl_good_plcp_health,
index d706b8afbe5abd7f54b570d1a414a7f9c3c9aac5..5391b4627397741d90bcd8b31ce3e5aa676ebb3b 100644 (file)
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *****************************************************************************/
-
+#include "iwl-agn.h"
 #include "iwl-agn-debugfs.h"
 
+static const char *fmt_value = "  %-30s %10u\n";
+static const char *fmt_hex   = "  %-30s       0x%02X\n";
+static const char *fmt_table = "  %-30s %10u  %10u  %10u  %10u\n";
+static const char *fmt_header =
+       "%-32s    current  cumulative       delta         max\n";
+
 static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz)
 {
        int p = 0;
@@ -121,436 +127,380 @@ ssize_t iwl_ucode_rx_stats_read(struct file *file, char __user *user_buf,
        }
 
        pos += iwl_statistics_flag(priv, buf, bufsz);
-       pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_Rx - OFDM:");
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "ina_cnt:", le32_to_cpu(ofdm->ina_cnt),
+                        fmt_header, "Statistics_Rx - OFDM:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "ina_cnt:",
+                        le32_to_cpu(ofdm->ina_cnt),
                         accum_ofdm->ina_cnt,
                         delta_ofdm->ina_cnt, max_ofdm->ina_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "fina_cnt:",
+                        fmt_table, "fina_cnt:",
                         le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt,
                         delta_ofdm->fina_cnt, max_ofdm->fina_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "plcp_err:",
+                        fmt_table, "plcp_err:",
                         le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err,
                         delta_ofdm->plcp_err, max_ofdm->plcp_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "crc32_err:",
+                        fmt_table, "crc32_err:",
                         le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err,
                         delta_ofdm->crc32_err, max_ofdm->crc32_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "overrun_err:",
+                        fmt_table, "overrun_err:",
                         le32_to_cpu(ofdm->overrun_err),
                         accum_ofdm->overrun_err, delta_ofdm->overrun_err,
                         max_ofdm->overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "early_overrun_err:",
+                        fmt_table, "early_overrun_err:",
                         le32_to_cpu(ofdm->early_overrun_err),
                         accum_ofdm->early_overrun_err,
                         delta_ofdm->early_overrun_err,
                         max_ofdm->early_overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "crc32_good:", le32_to_cpu(ofdm->crc32_good),
+                        fmt_table, "crc32_good:",
+                        le32_to_cpu(ofdm->crc32_good),
                         accum_ofdm->crc32_good, delta_ofdm->crc32_good,
                         max_ofdm->crc32_good);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "false_alarm_cnt:",
+                        fmt_table, "false_alarm_cnt:",
                         le32_to_cpu(ofdm->false_alarm_cnt),
                         accum_ofdm->false_alarm_cnt,
                         delta_ofdm->false_alarm_cnt,
                         max_ofdm->false_alarm_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "fina_sync_err_cnt:",
+                        fmt_table, "fina_sync_err_cnt:",
                         le32_to_cpu(ofdm->fina_sync_err_cnt),
                         accum_ofdm->fina_sync_err_cnt,
                         delta_ofdm->fina_sync_err_cnt,
                         max_ofdm->fina_sync_err_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sfd_timeout:",
+                        fmt_table, "sfd_timeout:",
                         le32_to_cpu(ofdm->sfd_timeout),
                         accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout,
                         max_ofdm->sfd_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "fina_timeout:",
+                        fmt_table, "fina_timeout:",
                         le32_to_cpu(ofdm->fina_timeout),
                         accum_ofdm->fina_timeout, delta_ofdm->fina_timeout,
                         max_ofdm->fina_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "unresponded_rts:",
+                        fmt_table, "unresponded_rts:",
                         le32_to_cpu(ofdm->unresponded_rts),
                         accum_ofdm->unresponded_rts,
                         delta_ofdm->unresponded_rts,
                         max_ofdm->unresponded_rts);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "rxe_frame_lmt_ovrun:",
+                        fmt_table, "rxe_frame_lmt_ovrun:",
                         le32_to_cpu(ofdm->rxe_frame_limit_overrun),
                         accum_ofdm->rxe_frame_limit_overrun,
                         delta_ofdm->rxe_frame_limit_overrun,
                         max_ofdm->rxe_frame_limit_overrun);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sent_ack_cnt:",
+                        fmt_table, "sent_ack_cnt:",
                         le32_to_cpu(ofdm->sent_ack_cnt),
                         accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt,
                         max_ofdm->sent_ack_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sent_cts_cnt:",
+                        fmt_table, "sent_cts_cnt:",
                         le32_to_cpu(ofdm->sent_cts_cnt),
                         accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt,
                         max_ofdm->sent_cts_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "sent_ba_rsp_cnt:",
+                        fmt_table, "sent_ba_rsp_cnt:",
                         le32_to_cpu(ofdm->sent_ba_rsp_cnt),
                         accum_ofdm->sent_ba_rsp_cnt,
                         delta_ofdm->sent_ba_rsp_cnt,
                         max_ofdm->sent_ba_rsp_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "dsp_self_kill:",
+                        fmt_table, "dsp_self_kill:",
                         le32_to_cpu(ofdm->dsp_self_kill),
                         accum_ofdm->dsp_self_kill,
                         delta_ofdm->dsp_self_kill,
                         max_ofdm->dsp_self_kill);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "mh_format_err:",
+                        fmt_table, "mh_format_err:",
                         le32_to_cpu(ofdm->mh_format_err),
                         accum_ofdm->mh_format_err,
                         delta_ofdm->mh_format_err,
                         max_ofdm->mh_format_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "re_acq_main_rssi_sum:",
+                        fmt_table, "re_acq_main_rssi_sum:",
                         le32_to_cpu(ofdm->re_acq_main_rssi_sum),
                         accum_ofdm->re_acq_main_rssi_sum,
                         delta_ofdm->re_acq_main_rssi_sum,
                         max_ofdm->re_acq_main_rssi_sum);
 
-       pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_Rx - CCK:");
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "ina_cnt:",
+                        fmt_header, "Statistics_Rx - CCK:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "ina_cnt:",
                         le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt,
                         delta_cck->ina_cnt, max_cck->ina_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "fina_cnt:",
+                        fmt_table, "fina_cnt:",
                         le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt,
                         delta_cck->fina_cnt, max_cck->fina_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "plcp_err:",
+                        fmt_table, "plcp_err:",
                         le32_to_cpu(cck->plcp_err), accum_cck->plcp_err,
                         delta_cck->plcp_err, max_cck->plcp_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "crc32_err:",
+                        fmt_table, "crc32_err:",
                         le32_to_cpu(cck->crc32_err), accum_cck->crc32_err,
                         delta_cck->crc32_err, max_cck->crc32_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "overrun_err:",
+                        fmt_table, "overrun_err:",
                         le32_to_cpu(cck->overrun_err),
                         accum_cck->overrun_err, delta_cck->overrun_err,
                         max_cck->overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "early_overrun_err:",
+                        fmt_table, "early_overrun_err:",
                         le32_to_cpu(cck->early_overrun_err),
                         accum_cck->early_overrun_err,
                         delta_cck->early_overrun_err,
                         max_cck->early_overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "crc32_good:",
+                        fmt_table, "crc32_good:",
                         le32_to_cpu(cck->crc32_good), accum_cck->crc32_good,
                         delta_cck->crc32_good, max_cck->crc32_good);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "false_alarm_cnt:",
+                        fmt_table, "false_alarm_cnt:",
                         le32_to_cpu(cck->false_alarm_cnt),
                         accum_cck->false_alarm_cnt,
                         delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "fina_sync_err_cnt:",
+                        fmt_table, "fina_sync_err_cnt:",
                         le32_to_cpu(cck->fina_sync_err_cnt),
                         accum_cck->fina_sync_err_cnt,
                         delta_cck->fina_sync_err_cnt,
                         max_cck->fina_sync_err_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "sfd_timeout:",
+                        fmt_table, "sfd_timeout:",
                         le32_to_cpu(cck->sfd_timeout),
                         accum_cck->sfd_timeout, delta_cck->sfd_timeout,
                         max_cck->sfd_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "fina_timeout:",
+                        fmt_table, "fina_timeout:",
                         le32_to_cpu(cck->fina_timeout),
                         accum_cck->fina_timeout, delta_cck->fina_timeout,
                         max_cck->fina_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "unresponded_rts:",
+                        fmt_table, "unresponded_rts:",
                         le32_to_cpu(cck->unresponded_rts),
                         accum_cck->unresponded_rts, delta_cck->unresponded_rts,
                         max_cck->unresponded_rts);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "rxe_frame_lmt_ovrun:",
+                        fmt_table, "rxe_frame_lmt_ovrun:",
                         le32_to_cpu(cck->rxe_frame_limit_overrun),
                         accum_cck->rxe_frame_limit_overrun,
                         delta_cck->rxe_frame_limit_overrun,
                         max_cck->rxe_frame_limit_overrun);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sent_ack_cnt:",
+                        fmt_table, "sent_ack_cnt:",
                         le32_to_cpu(cck->sent_ack_cnt),
                         accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt,
                         max_cck->sent_ack_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sent_cts_cnt:",
+                        fmt_table, "sent_cts_cnt:",
                         le32_to_cpu(cck->sent_cts_cnt),
                         accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt,
                         max_cck->sent_cts_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "sent_ba_rsp_cnt:",
+                        fmt_table, "sent_ba_rsp_cnt:",
                         le32_to_cpu(cck->sent_ba_rsp_cnt),
                         accum_cck->sent_ba_rsp_cnt,
                         delta_cck->sent_ba_rsp_cnt,
                         max_cck->sent_ba_rsp_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "dsp_self_kill:",
+                        fmt_table, "dsp_self_kill:",
                         le32_to_cpu(cck->dsp_self_kill),
                         accum_cck->dsp_self_kill, delta_cck->dsp_self_kill,
                         max_cck->dsp_self_kill);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "mh_format_err:",
+                        fmt_table, "mh_format_err:",
                         le32_to_cpu(cck->mh_format_err),
                         accum_cck->mh_format_err, delta_cck->mh_format_err,
                         max_cck->mh_format_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "re_acq_main_rssi_sum:",
+                        fmt_table, "re_acq_main_rssi_sum:",
                         le32_to_cpu(cck->re_acq_main_rssi_sum),
                         accum_cck->re_acq_main_rssi_sum,
                         delta_cck->re_acq_main_rssi_sum,
                         max_cck->re_acq_main_rssi_sum);
 
-       pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_Rx - GENERAL:");
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "bogus_cts:",
+                        fmt_header, "Statistics_Rx - GENERAL:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "bogus_cts:",
                         le32_to_cpu(general->bogus_cts),
                         accum_general->bogus_cts, delta_general->bogus_cts,
                         max_general->bogus_cts);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n", "bogus_ack:",
+                        fmt_table, "bogus_ack:",
                         le32_to_cpu(general->bogus_ack),
                         accum_general->bogus_ack, delta_general->bogus_ack,
                         max_general->bogus_ack);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "non_bssid_frames:",
+                        fmt_table, "non_bssid_frames:",
                         le32_to_cpu(general->non_bssid_frames),
                         accum_general->non_bssid_frames,
                         delta_general->non_bssid_frames,
                         max_general->non_bssid_frames);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "filtered_frames:",
+                        fmt_table, "filtered_frames:",
                         le32_to_cpu(general->filtered_frames),
                         accum_general->filtered_frames,
                         delta_general->filtered_frames,
                         max_general->filtered_frames);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "non_channel_beacons:",
+                        fmt_table, "non_channel_beacons:",
                         le32_to_cpu(general->non_channel_beacons),
                         accum_general->non_channel_beacons,
                         delta_general->non_channel_beacons,
                         max_general->non_channel_beacons);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "channel_beacons:",
+                        fmt_table, "channel_beacons:",
                         le32_to_cpu(general->channel_beacons),
                         accum_general->channel_beacons,
                         delta_general->channel_beacons,
                         max_general->channel_beacons);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "num_missed_bcon:",
+                        fmt_table, "num_missed_bcon:",
                         le32_to_cpu(general->num_missed_bcon),
                         accum_general->num_missed_bcon,
                         delta_general->num_missed_bcon,
                         max_general->num_missed_bcon);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "adc_rx_saturation_time:",
+                        fmt_table, "adc_rx_saturation_time:",
                         le32_to_cpu(general->adc_rx_saturation_time),
                         accum_general->adc_rx_saturation_time,
                         delta_general->adc_rx_saturation_time,
                         max_general->adc_rx_saturation_time);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "ina_detect_search_tm:",
+                        fmt_table, "ina_detect_search_tm:",
                         le32_to_cpu(general->ina_detection_search_time),
                         accum_general->ina_detection_search_time,
                         delta_general->ina_detection_search_time,
                         max_general->ina_detection_search_time);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_silence_rssi_a:",
+                        fmt_table, "beacon_silence_rssi_a:",
                         le32_to_cpu(general->beacon_silence_rssi_a),
                         accum_general->beacon_silence_rssi_a,
                         delta_general->beacon_silence_rssi_a,
                         max_general->beacon_silence_rssi_a);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_silence_rssi_b:",
+                        fmt_table, "beacon_silence_rssi_b:",
                         le32_to_cpu(general->beacon_silence_rssi_b),
                         accum_general->beacon_silence_rssi_b,
                         delta_general->beacon_silence_rssi_b,
                         max_general->beacon_silence_rssi_b);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_silence_rssi_c:",
+                        fmt_table, "beacon_silence_rssi_c:",
                         le32_to_cpu(general->beacon_silence_rssi_c),
                         accum_general->beacon_silence_rssi_c,
                         delta_general->beacon_silence_rssi_c,
                         max_general->beacon_silence_rssi_c);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "interference_data_flag:",
+                        fmt_table, "interference_data_flag:",
                         le32_to_cpu(general->interference_data_flag),
                         accum_general->interference_data_flag,
                         delta_general->interference_data_flag,
                         max_general->interference_data_flag);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "channel_load:",
+                        fmt_table, "channel_load:",
                         le32_to_cpu(general->channel_load),
                         accum_general->channel_load,
                         delta_general->channel_load,
                         max_general->channel_load);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "dsp_false_alarms:",
+                        fmt_table, "dsp_false_alarms:",
                         le32_to_cpu(general->dsp_false_alarms),
                         accum_general->dsp_false_alarms,
                         delta_general->dsp_false_alarms,
                         max_general->dsp_false_alarms);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_rssi_a:",
+                        fmt_table, "beacon_rssi_a:",
                         le32_to_cpu(general->beacon_rssi_a),
                         accum_general->beacon_rssi_a,
                         delta_general->beacon_rssi_a,
                         max_general->beacon_rssi_a);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_rssi_b:",
+                        fmt_table, "beacon_rssi_b:",
                         le32_to_cpu(general->beacon_rssi_b),
                         accum_general->beacon_rssi_b,
                         delta_general->beacon_rssi_b,
                         max_general->beacon_rssi_b);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_rssi_c:",
+                        fmt_table, "beacon_rssi_c:",
                         le32_to_cpu(general->beacon_rssi_c),
                         accum_general->beacon_rssi_c,
                         delta_general->beacon_rssi_c,
                         max_general->beacon_rssi_c);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_energy_a:",
+                        fmt_table, "beacon_energy_a:",
                         le32_to_cpu(general->beacon_energy_a),
                         accum_general->beacon_energy_a,
                         delta_general->beacon_energy_a,
                         max_general->beacon_energy_a);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_energy_b:",
+                        fmt_table, "beacon_energy_b:",
                         le32_to_cpu(general->beacon_energy_b),
                         accum_general->beacon_energy_b,
                         delta_general->beacon_energy_b,
                         max_general->beacon_energy_b);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "beacon_energy_c:",
+                        fmt_table, "beacon_energy_c:",
                         le32_to_cpu(general->beacon_energy_c),
                         accum_general->beacon_energy_c,
                         delta_general->beacon_energy_c,
                         max_general->beacon_energy_c);
 
-       pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n");
-       pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_Rx - OFDM_HT:");
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "plcp_err:",
+                        fmt_header, "Statistics_Rx - OFDM_HT:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "plcp_err:",
                         le32_to_cpu(ht->plcp_err), accum_ht->plcp_err,
                         delta_ht->plcp_err, max_ht->plcp_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "overrun_err:",
+                        fmt_table, "overrun_err:",
                         le32_to_cpu(ht->overrun_err), accum_ht->overrun_err,
                         delta_ht->overrun_err, max_ht->overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "early_overrun_err:",
+                        fmt_table, "early_overrun_err:",
                         le32_to_cpu(ht->early_overrun_err),
                         accum_ht->early_overrun_err,
                         delta_ht->early_overrun_err,
                         max_ht->early_overrun_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "crc32_good:",
+                        fmt_table, "crc32_good:",
                         le32_to_cpu(ht->crc32_good), accum_ht->crc32_good,
                         delta_ht->crc32_good, max_ht->crc32_good);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "crc32_err:",
+                        fmt_table, "crc32_err:",
                         le32_to_cpu(ht->crc32_err), accum_ht->crc32_err,
                         delta_ht->crc32_err, max_ht->crc32_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "mh_format_err:",
+                        fmt_table, "mh_format_err:",
                         le32_to_cpu(ht->mh_format_err),
                         accum_ht->mh_format_err,
                         delta_ht->mh_format_err, max_ht->mh_format_err);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg_crc32_good:",
+                        fmt_table, "agg_crc32_good:",
                         le32_to_cpu(ht->agg_crc32_good),
                         accum_ht->agg_crc32_good,
                         delta_ht->agg_crc32_good, max_ht->agg_crc32_good);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg_mpdu_cnt:",
+                        fmt_table, "agg_mpdu_cnt:",
                         le32_to_cpu(ht->agg_mpdu_cnt),
                         accum_ht->agg_mpdu_cnt,
                         delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg_cnt:",
+                        fmt_table, "agg_cnt:",
                         le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt,
                         delta_ht->agg_cnt, max_ht->agg_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "unsupport_mcs:",
+                        fmt_table, "unsupport_mcs:",
                         le32_to_cpu(ht->unsupport_mcs),
                         accum_ht->unsupport_mcs,
                         delta_ht->unsupport_mcs, max_ht->unsupport_mcs);
@@ -597,166 +547,141 @@ ssize_t iwl_ucode_tx_stats_read(struct file *file,
        }
 
        pos += iwl_statistics_flag(priv, buf, bufsz);
-       pos += scnprintf(buf + pos, bufsz - pos,  "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_Tx:");
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "preamble:",
+                        fmt_header, "Statistics_Tx:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "preamble:",
                         le32_to_cpu(tx->preamble_cnt),
                         accum_tx->preamble_cnt,
                         delta_tx->preamble_cnt, max_tx->preamble_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "rx_detected_cnt:",
+                        fmt_table, "rx_detected_cnt:",
                         le32_to_cpu(tx->rx_detected_cnt),
                         accum_tx->rx_detected_cnt,
                         delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "bt_prio_defer_cnt:",
+                        fmt_table, "bt_prio_defer_cnt:",
                         le32_to_cpu(tx->bt_prio_defer_cnt),
                         accum_tx->bt_prio_defer_cnt,
                         delta_tx->bt_prio_defer_cnt,
                         max_tx->bt_prio_defer_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "bt_prio_kill_cnt:",
+                        fmt_table, "bt_prio_kill_cnt:",
                         le32_to_cpu(tx->bt_prio_kill_cnt),
                         accum_tx->bt_prio_kill_cnt,
                         delta_tx->bt_prio_kill_cnt,
                         max_tx->bt_prio_kill_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "few_bytes_cnt:",
+                        fmt_table, "few_bytes_cnt:",
                         le32_to_cpu(tx->few_bytes_cnt),
                         accum_tx->few_bytes_cnt,
                         delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "cts_timeout:",
+                        fmt_table, "cts_timeout:",
                         le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout,
                         delta_tx->cts_timeout, max_tx->cts_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "ack_timeout:",
+                        fmt_table, "ack_timeout:",
                         le32_to_cpu(tx->ack_timeout),
                         accum_tx->ack_timeout,
                         delta_tx->ack_timeout, max_tx->ack_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "expected_ack_cnt:",
+                        fmt_table, "expected_ack_cnt:",
                         le32_to_cpu(tx->expected_ack_cnt),
                         accum_tx->expected_ack_cnt,
                         delta_tx->expected_ack_cnt,
                         max_tx->expected_ack_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "actual_ack_cnt:",
+                        fmt_table, "actual_ack_cnt:",
                         le32_to_cpu(tx->actual_ack_cnt),
                         accum_tx->actual_ack_cnt,
                         delta_tx->actual_ack_cnt,
                         max_tx->actual_ack_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "dump_msdu_cnt:",
+                        fmt_table, "dump_msdu_cnt:",
                         le32_to_cpu(tx->dump_msdu_cnt),
                         accum_tx->dump_msdu_cnt,
                         delta_tx->dump_msdu_cnt,
                         max_tx->dump_msdu_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "abort_nxt_frame_mismatch:",
+                        fmt_table, "abort_nxt_frame_mismatch:",
                         le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt),
                         accum_tx->burst_abort_next_frame_mismatch_cnt,
                         delta_tx->burst_abort_next_frame_mismatch_cnt,
                         max_tx->burst_abort_next_frame_mismatch_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "abort_missing_nxt_frame:",
+                        fmt_table, "abort_missing_nxt_frame:",
                         le32_to_cpu(tx->burst_abort_missing_next_frame_cnt),
                         accum_tx->burst_abort_missing_next_frame_cnt,
                         delta_tx->burst_abort_missing_next_frame_cnt,
                         max_tx->burst_abort_missing_next_frame_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "cts_timeout_collision:",
+                        fmt_table, "cts_timeout_collision:",
                         le32_to_cpu(tx->cts_timeout_collision),
                         accum_tx->cts_timeout_collision,
                         delta_tx->cts_timeout_collision,
                         max_tx->cts_timeout_collision);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "ack_ba_timeout_collision:",
+                        fmt_table, "ack_ba_timeout_collision:",
                         le32_to_cpu(tx->ack_or_ba_timeout_collision),
                         accum_tx->ack_or_ba_timeout_collision,
                         delta_tx->ack_or_ba_timeout_collision,
                         max_tx->ack_or_ba_timeout_collision);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg ba_timeout:",
+                        fmt_table, "agg ba_timeout:",
                         le32_to_cpu(tx->agg.ba_timeout),
                         accum_tx->agg.ba_timeout,
                         delta_tx->agg.ba_timeout,
                         max_tx->agg.ba_timeout);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg ba_resched_frames:",
+                        fmt_table, "agg ba_resched_frames:",
                         le32_to_cpu(tx->agg.ba_reschedule_frames),
                         accum_tx->agg.ba_reschedule_frames,
                         delta_tx->agg.ba_reschedule_frames,
                         max_tx->agg.ba_reschedule_frames);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg scd_query_agg_frame:",
+                        fmt_table, "agg scd_query_agg_frame:",
                         le32_to_cpu(tx->agg.scd_query_agg_frame_cnt),
                         accum_tx->agg.scd_query_agg_frame_cnt,
                         delta_tx->agg.scd_query_agg_frame_cnt,
                         max_tx->agg.scd_query_agg_frame_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg scd_query_no_agg:",
+                        fmt_table, "agg scd_query_no_agg:",
                         le32_to_cpu(tx->agg.scd_query_no_agg),
                         accum_tx->agg.scd_query_no_agg,
                         delta_tx->agg.scd_query_no_agg,
                         max_tx->agg.scd_query_no_agg);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg scd_query_agg:",
+                        fmt_table, "agg scd_query_agg:",
                         le32_to_cpu(tx->agg.scd_query_agg),
                         accum_tx->agg.scd_query_agg,
                         delta_tx->agg.scd_query_agg,
                         max_tx->agg.scd_query_agg);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg scd_query_mismatch:",
+                        fmt_table, "agg scd_query_mismatch:",
                         le32_to_cpu(tx->agg.scd_query_mismatch),
                         accum_tx->agg.scd_query_mismatch,
                         delta_tx->agg.scd_query_mismatch,
                         max_tx->agg.scd_query_mismatch);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg frame_not_ready:",
+                        fmt_table, "agg frame_not_ready:",
                         le32_to_cpu(tx->agg.frame_not_ready),
                         accum_tx->agg.frame_not_ready,
                         delta_tx->agg.frame_not_ready,
                         max_tx->agg.frame_not_ready);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg underrun:",
+                        fmt_table, "agg underrun:",
                         le32_to_cpu(tx->agg.underrun),
                         accum_tx->agg.underrun,
                         delta_tx->agg.underrun, max_tx->agg.underrun);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg bt_prio_kill:",
+                        fmt_table, "agg bt_prio_kill:",
                         le32_to_cpu(tx->agg.bt_prio_kill),
                         accum_tx->agg.bt_prio_kill,
                         delta_tx->agg.bt_prio_kill,
                         max_tx->agg.bt_prio_kill);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "agg rx_ba_rsp_cnt:",
+                        fmt_table, "agg rx_ba_rsp_cnt:",
                         le32_to_cpu(tx->agg.rx_ba_rsp_cnt),
                         accum_tx->agg.rx_ba_rsp_cnt,
                         delta_tx->agg.rx_ba_rsp_cnt,
@@ -767,15 +692,15 @@ ssize_t iwl_ucode_tx_stats_read(struct file *file,
                        "tx power: (1/2 dB step)\n");
                if ((priv->cfg->valid_tx_ant & ANT_A) && tx->tx_power.ant_a)
                        pos += scnprintf(buf + pos, bufsz - pos,
-                                       "\tantenna A: 0x%X\n",
+                                       fmt_hex, "antenna A:",
                                        tx->tx_power.ant_a);
                if ((priv->cfg->valid_tx_ant & ANT_B) && tx->tx_power.ant_b)
                        pos += scnprintf(buf + pos, bufsz - pos,
-                                       "\tantenna B: 0x%X\n",
+                                       fmt_hex, "antenna B:",
                                        tx->tx_power.ant_b);
                if ((priv->cfg->valid_tx_ant & ANT_C) && tx->tx_power.ant_c)
                        pos += scnprintf(buf + pos, bufsz - pos,
-                                       "\tantenna C: 0x%X\n",
+                                       fmt_hex, "antenna C:",
                                        tx->tx_power.ant_c);
        }
        ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
@@ -838,84 +763,72 @@ ssize_t iwl_ucode_general_stats_read(struct file *file, char __user *user_buf,
        }
 
        pos += iwl_statistics_flag(priv, buf, bufsz);
-       pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
-                        "acumulative       delta         max\n",
-                        "Statistics_General:");
-       pos += scnprintf(buf + pos, bufsz - pos, "  %-30s %10u\n",
-                        "temperature:",
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_header, "Statistics_General:");
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_value, "temperature:",
                         le32_to_cpu(general->temperature));
-       pos += scnprintf(buf + pos, bufsz - pos, "  %-30s %10u\n",
-                        "temperature_m:",
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_value, "temperature_m:",
                         le32_to_cpu(general->temperature_m));
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "burst_check:",
+                        fmt_value, "ttl_timestamp:",
+                        le32_to_cpu(general->ttl_timestamp));
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        fmt_table, "burst_check:",
                         le32_to_cpu(dbg->burst_check),
                         accum_dbg->burst_check,
                         delta_dbg->burst_check, max_dbg->burst_check);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "burst_count:",
+                        fmt_table, "burst_count:",
                         le32_to_cpu(dbg->burst_count),
                         accum_dbg->burst_count,
                         delta_dbg->burst_count, max_dbg->burst_count);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "wait_for_silence_timeout_count:",
+                        fmt_table, "wait_for_silence_timeout_count:",
                         le32_to_cpu(dbg->wait_for_silence_timeout_cnt),
                         accum_dbg->wait_for_silence_timeout_cnt,
                         delta_dbg->wait_for_silence_timeout_cnt,
                         max_dbg->wait_for_silence_timeout_cnt);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "sleep_time:",
+                        fmt_table, "sleep_time:",
                         le32_to_cpu(general->sleep_time),
                         accum_general->sleep_time,
                         delta_general->sleep_time, max_general->sleep_time);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "slots_out:",
+                        fmt_table, "slots_out:",
                         le32_to_cpu(general->slots_out),
                         accum_general->slots_out,
                         delta_general->slots_out, max_general->slots_out);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "slots_idle:",
+                        fmt_table, "slots_idle:",
                         le32_to_cpu(general->slots_idle),
                         accum_general->slots_idle,
                         delta_general->slots_idle, max_general->slots_idle);
-       pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n",
-                        le32_to_cpu(general->ttl_timestamp));
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "tx_on_a:",
+                        fmt_table, "tx_on_a:",
                         le32_to_cpu(div->tx_on_a), accum_div->tx_on_a,
                         delta_div->tx_on_a, max_div->tx_on_a);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "tx_on_b:",
+                        fmt_table, "tx_on_b:",
                         le32_to_cpu(div->tx_on_b), accum_div->tx_on_b,
                         delta_div->tx_on_b, max_div->tx_on_b);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "exec_time:",
+                        fmt_table, "exec_time:",
                         le32_to_cpu(div->exec_time), accum_div->exec_time,
                         delta_div->exec_time, max_div->exec_time);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "probe_time:",
+                        fmt_table, "probe_time:",
                         le32_to_cpu(div->probe_time), accum_div->probe_time,
                         delta_div->probe_time, max_div->probe_time);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "rx_enable_counter:",
+                        fmt_table, "rx_enable_counter:",
                         le32_to_cpu(general->rx_enable_counter),
                         accum_general->rx_enable_counter,
                         delta_general->rx_enable_counter,
                         max_general->rx_enable_counter);
        pos += scnprintf(buf + pos, bufsz - pos,
-                        "  %-30s %10u  %10u  %10u  %10u\n",
-                        "num_of_sos_states:",
+                        fmt_table, "num_of_sos_states:",
                         le32_to_cpu(general->num_of_sos_states),
                         accum_general->num_of_sos_states,
                         delta_general->num_of_sos_states,
@@ -1011,3 +924,147 @@ ssize_t iwl_ucode_bt_stats_read(struct file *file,
        kfree(buf);
        return ret;
 }
+
+ssize_t iwl_reply_tx_error_read(struct file *file,
+                               char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+       int pos = 0;
+       char *buf;
+       int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) +
+               (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200;
+       ssize_t ret;
+
+       if (!iwl_is_alive(priv))
+               return -EAGAIN;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf) {
+               IWL_ERR(priv, "Can not allocate Buffer\n");
+               return -ENOMEM;
+       }
+
+       pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n");
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY),
+                        priv->_agn.reply_tx_stats.pp_delay);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES),
+                        priv->_agn.reply_tx_stats.pp_few_bytes);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO),
+                        priv->_agn.reply_tx_stats.pp_bt_prio);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD),
+                        priv->_agn.reply_tx_stats.pp_quiet_period);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK),
+                        priv->_agn.reply_tx_stats.pp_calc_ttak);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_tx_fail_reason(
+                               TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY),
+                        priv->_agn.reply_tx_stats.int_crossed_retry);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT),
+                        priv->_agn.reply_tx_stats.short_limit);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT),
+                        priv->_agn.reply_tx_stats.long_limit);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN),
+                        priv->_agn.reply_tx_stats.fifo_underrun);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW),
+                        priv->_agn.reply_tx_stats.drain_flow);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH),
+                        priv->_agn.reply_tx_stats.rfkill_flush);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE),
+                        priv->_agn.reply_tx_stats.life_expire);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS),
+                        priv->_agn.reply_tx_stats.dest_ps);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED),
+                        priv->_agn.reply_tx_stats.host_abort);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY),
+                        priv->_agn.reply_tx_stats.pp_delay);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID),
+                        priv->_agn.reply_tx_stats.sta_invalid);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED),
+                        priv->_agn.reply_tx_stats.frag_drop);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE),
+                        priv->_agn.reply_tx_stats.tid_disable);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED),
+                        priv->_agn.reply_tx_stats.fifo_flush);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_tx_fail_reason(
+                               TX_STATUS_FAIL_INSUFFICIENT_CF_POLL),
+                        priv->_agn.reply_tx_stats.insuff_cf_poll);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX),
+                        priv->_agn.reply_tx_stats.fail_hw_drop);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_tx_fail_reason(
+                               TX_STATUS_FAIL_NO_BEACON_ON_RADAR),
+                        priv->_agn.reply_tx_stats.sta_color_mismatch);
+       pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+                        priv->_agn.reply_tx_stats.unknown);
+
+       pos += scnprintf(buf + pos, bufsz - pos,
+                        "\nStatistics_Agg_TX_Error:\n");
+
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK),
+                        priv->_agn.reply_agg_tx_stats.underrun);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK),
+                        priv->_agn.reply_agg_tx_stats.bt_prio);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK),
+                        priv->_agn.reply_agg_tx_stats.few_bytes);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK),
+                        priv->_agn.reply_agg_tx_stats.abort);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(
+                               AGG_TX_STATE_LAST_SENT_TTL_MSK),
+                        priv->_agn.reply_agg_tx_stats.last_sent_ttl);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(
+                               AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK),
+                        priv->_agn.reply_agg_tx_stats.last_sent_try);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(
+                               AGG_TX_STATE_LAST_SENT_BT_KILL_MSK),
+                        priv->_agn.reply_agg_tx_stats.last_sent_bt_kill);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK),
+                        priv->_agn.reply_agg_tx_stats.scd_query);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(
+                               AGG_TX_STATE_TEST_BAD_CRC32_MSK),
+                        priv->_agn.reply_agg_tx_stats.bad_crc32);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK),
+                        priv->_agn.reply_agg_tx_stats.response);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK),
+                        priv->_agn.reply_agg_tx_stats.dump_tx);
+       pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n",
+                        iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK),
+                        priv->_agn.reply_agg_tx_stats.delay_tx);
+       pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n",
+                        priv->_agn.reply_agg_tx_stats.unknown);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
index bbdce5913ac77a51d52fb02cbef8a587287b6b8e..f2573b5486cd20bc958367c33cf0a9e577ba643a 100644 (file)
@@ -39,6 +39,8 @@ ssize_t iwl_ucode_general_stats_read(struct file *file, char __user *user_buf,
                                     size_t count, loff_t *ppos);
 ssize_t iwl_ucode_bt_stats_read(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos);
+ssize_t iwl_reply_tx_error_read(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos);
 #else
 static ssize_t iwl_ucode_rx_stats_read(struct file *file, char __user *user_buf,
                                       size_t count, loff_t *ppos)
@@ -60,4 +62,9 @@ static ssize_t iwl_ucode_bt_stats_read(struct file *file, char __user *user_buf,
 {
        return 0;
 }
+static ssize_t iwl_reply_tx_error_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       return 0;
+}
 #endif
index 6fb52abafc8d46e305328754564d25827cd8a16a..d86902b83630d3163f45bb07b224634fa14d2817 100644 (file)
@@ -287,6 +287,15 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
        ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS];
        ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN];
 
+       /*
+        * If the PAN context is inactive, then we don't need
+        * to update the PAN parameters, the last thing we'll
+        * have done before it goes inactive is making the PAN
+        * parameters be WLAN-only.
+        */
+       if (!ctx_pan->is_active)
+               return 0;
+
        memset(&cmd, 0, sizeof(cmd));
 
        /* only 2 slots are currently allowed */
@@ -312,7 +321,7 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
                        bcnint = max_t(int, bcnint,
                                       ctx_bss->vif->bss_conf.beacon_int);
                if (!bcnint)
-                       bcnint = 100;
+                       bcnint = DEFAULT_BEACON_INTERVAL;
                slot0 = bcnint / 2;
                slot1 = bcnint - slot0;
 
@@ -330,7 +339,12 @@ static int iwlagn_set_pan_params(struct iwl_priv *priv)
                slot0 = 0;
                slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) *
                                        ctx_pan->vif->bss_conf.beacon_int;
-               slot1 = max_t(int, 100, slot1);
+               slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1);
+
+               if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+                       slot0 = slot1 * 3 - 20;
+                       slot1 = 20;
+               }
        }
 
        cmd.slots[0].width = cpu_to_le16(slot0);
index a8f2adfd799e790e009192a5bed1594ed602e87c..299fd9d596041ed2bc0758393148b1ddf7a2efea 100644 (file)
@@ -46,6 +46,181 @@ static inline u32 iwlagn_get_scd_ssn(struct iwl5000_tx_resp *tx_resp)
                            tx_resp->frame_count) & MAX_SN;
 }
 
+static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+       status &= TX_STATUS_MSK;
+
+       switch (status) {
+       case TX_STATUS_POSTPONE_DELAY:
+               priv->_agn.reply_tx_stats.pp_delay++;
+               break;
+       case TX_STATUS_POSTPONE_FEW_BYTES:
+               priv->_agn.reply_tx_stats.pp_few_bytes++;
+               break;
+       case TX_STATUS_POSTPONE_BT_PRIO:
+               priv->_agn.reply_tx_stats.pp_bt_prio++;
+               break;
+       case TX_STATUS_POSTPONE_QUIET_PERIOD:
+               priv->_agn.reply_tx_stats.pp_quiet_period++;
+               break;
+       case TX_STATUS_POSTPONE_CALC_TTAK:
+               priv->_agn.reply_tx_stats.pp_calc_ttak++;
+               break;
+       case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+               priv->_agn.reply_tx_stats.int_crossed_retry++;
+               break;
+       case TX_STATUS_FAIL_SHORT_LIMIT:
+               priv->_agn.reply_tx_stats.short_limit++;
+               break;
+       case TX_STATUS_FAIL_LONG_LIMIT:
+               priv->_agn.reply_tx_stats.long_limit++;
+               break;
+       case TX_STATUS_FAIL_FIFO_UNDERRUN:
+               priv->_agn.reply_tx_stats.fifo_underrun++;
+               break;
+       case TX_STATUS_FAIL_DRAIN_FLOW:
+               priv->_agn.reply_tx_stats.drain_flow++;
+               break;
+       case TX_STATUS_FAIL_RFKILL_FLUSH:
+               priv->_agn.reply_tx_stats.rfkill_flush++;
+               break;
+       case TX_STATUS_FAIL_LIFE_EXPIRE:
+               priv->_agn.reply_tx_stats.life_expire++;
+               break;
+       case TX_STATUS_FAIL_DEST_PS:
+               priv->_agn.reply_tx_stats.dest_ps++;
+               break;
+       case TX_STATUS_FAIL_HOST_ABORTED:
+               priv->_agn.reply_tx_stats.host_abort++;
+               break;
+       case TX_STATUS_FAIL_BT_RETRY:
+               priv->_agn.reply_tx_stats.bt_retry++;
+               break;
+       case TX_STATUS_FAIL_STA_INVALID:
+               priv->_agn.reply_tx_stats.sta_invalid++;
+               break;
+       case TX_STATUS_FAIL_FRAG_DROPPED:
+               priv->_agn.reply_tx_stats.frag_drop++;
+               break;
+       case TX_STATUS_FAIL_TID_DISABLE:
+               priv->_agn.reply_tx_stats.tid_disable++;
+               break;
+       case TX_STATUS_FAIL_FIFO_FLUSHED:
+               priv->_agn.reply_tx_stats.fifo_flush++;
+               break;
+       case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL:
+               priv->_agn.reply_tx_stats.insuff_cf_poll++;
+               break;
+       case TX_STATUS_FAIL_PASSIVE_NO_RX:
+               priv->_agn.reply_tx_stats.fail_hw_drop++;
+               break;
+       case TX_STATUS_FAIL_NO_BEACON_ON_RADAR:
+               priv->_agn.reply_tx_stats.sta_color_mismatch++;
+               break;
+       default:
+               priv->_agn.reply_tx_stats.unknown++;
+               break;
+       }
+}
+
+static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
+{
+       status &= AGG_TX_STATUS_MSK;
+
+       switch (status) {
+       case AGG_TX_STATE_UNDERRUN_MSK:
+               priv->_agn.reply_agg_tx_stats.underrun++;
+               break;
+       case AGG_TX_STATE_BT_PRIO_MSK:
+               priv->_agn.reply_agg_tx_stats.bt_prio++;
+               break;
+       case AGG_TX_STATE_FEW_BYTES_MSK:
+               priv->_agn.reply_agg_tx_stats.few_bytes++;
+               break;
+       case AGG_TX_STATE_ABORT_MSK:
+               priv->_agn.reply_agg_tx_stats.abort++;
+               break;
+       case AGG_TX_STATE_LAST_SENT_TTL_MSK:
+               priv->_agn.reply_agg_tx_stats.last_sent_ttl++;
+               break;
+       case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK:
+               priv->_agn.reply_agg_tx_stats.last_sent_try++;
+               break;
+       case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK:
+               priv->_agn.reply_agg_tx_stats.last_sent_bt_kill++;
+               break;
+       case AGG_TX_STATE_SCD_QUERY_MSK:
+               priv->_agn.reply_agg_tx_stats.scd_query++;
+               break;
+       case AGG_TX_STATE_TEST_BAD_CRC32_MSK:
+               priv->_agn.reply_agg_tx_stats.bad_crc32++;
+               break;
+       case AGG_TX_STATE_RESPONSE_MSK:
+               priv->_agn.reply_agg_tx_stats.response++;
+               break;
+       case AGG_TX_STATE_DUMP_TX_MSK:
+               priv->_agn.reply_agg_tx_stats.dump_tx++;
+               break;
+       case AGG_TX_STATE_DELAY_TX_MSK:
+               priv->_agn.reply_agg_tx_stats.delay_tx++;
+               break;
+       default:
+               priv->_agn.reply_agg_tx_stats.unknown++;
+               break;
+       }
+}
+
+static void iwlagn_set_tx_status(struct iwl_priv *priv,
+                                struct ieee80211_tx_info *info,
+                                struct iwl5000_tx_resp *tx_resp,
+                                int txq_id, bool is_agg)
+{
+       u16  status = le16_to_cpu(tx_resp->status.status);
+
+       info->status.rates[0].count = tx_resp->failure_frame + 1;
+       if (is_agg)
+               info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+       info->flags |= iwl_tx_status_to_mac80211(status);
+       iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
+                                   info);
+       if (!iwl_is_tx_success(status))
+               iwlagn_count_tx_err_status(priv, status);
+
+       IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
+                          "0x%x retries %d\n",
+                          txq_id,
+                          iwl_get_tx_fail_reason(status), status,
+                          le32_to_cpu(tx_resp->rate_n_flags),
+                          tx_resp->failure_frame);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x
+
+const char *iwl_get_agg_tx_fail_reason(u16 status)
+{
+       status &= AGG_TX_STATUS_MSK;
+       switch (status) {
+       case AGG_TX_STATE_TRANSMITTED:
+               return "SUCCESS";
+               AGG_TX_STATE_FAIL(UNDERRUN_MSK);
+               AGG_TX_STATE_FAIL(BT_PRIO_MSK);
+               AGG_TX_STATE_FAIL(FEW_BYTES_MSK);
+               AGG_TX_STATE_FAIL(ABORT_MSK);
+               AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK);
+               AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK);
+               AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK);
+               AGG_TX_STATE_FAIL(SCD_QUERY_MSK);
+               AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK);
+               AGG_TX_STATE_FAIL(RESPONSE_MSK);
+               AGG_TX_STATE_FAIL(DUMP_TX_MSK);
+               AGG_TX_STATE_FAIL(DELAY_TX_MSK);
+       }
+
+       return "UNKNOWN";
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
 static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
                                      struct iwl_ht_agg *agg,
                                      struct iwl5000_tx_resp *tx_resp,
@@ -53,9 +228,7 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
 {
        u16 status;
        struct agg_tx_status *frame_status = &tx_resp->status;
-       struct ieee80211_tx_info *info = NULL;
        struct ieee80211_hdr *hdr = NULL;
-       u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
        int i, sh, idx;
        u16 seq;
 
@@ -64,31 +237,20 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
 
        agg->frame_count = tx_resp->frame_count;
        agg->start_idx = start_idx;
-       agg->rate_n_flags = rate_n_flags;
+       agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags);
        agg->bitmap = 0;
 
        /* # frames attempted by Tx command */
        if (agg->frame_count == 1) {
                /* Only one frame was attempted; no block-ack will arrive */
-               status = le16_to_cpu(frame_status[0].status);
                idx = start_idx;
 
-               /* FIXME: code repetition */
                IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
                                   agg->frame_count, agg->start_idx, idx);
-
-               info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb);
-               info->status.rates[0].count = tx_resp->failure_frame + 1;
-               info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-               info->flags |= iwl_tx_status_to_mac80211(status);
-               iwlagn_hwrate_to_tx_control(priv, rate_n_flags, info);
-
-               /* FIXME: code repetition end */
-
-               IWL_DEBUG_TX_REPLY(priv, "1 Frame 0x%x failure :%d\n",
-                                   status & 0xff, tx_resp->failure_frame);
-               IWL_DEBUG_TX_REPLY(priv, "Rate Info rate_n_flags=%x\n", rate_n_flags);
-
+               iwlagn_set_tx_status(priv,
+                                    IEEE80211_SKB_CB(
+                                       priv->txq[txq_id].txb[idx].skb),
+                                    tx_resp, txq_id, true);
                agg->wait_for_ba = 0;
        } else {
                /* Two or more frames were attempted; expect block-ack */
@@ -109,12 +271,20 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
                        idx = SEQ_TO_INDEX(seq);
                        txq_id = SEQ_TO_QUEUE(seq);
 
+                       if (status & AGG_TX_STATUS_MSK)
+                               iwlagn_count_agg_tx_err_status(priv, status);
+
                        if (status & (AGG_TX_STATE_FEW_BYTES_MSK |
                                      AGG_TX_STATE_ABORT_MSK))
                                continue;
 
                        IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n",
                                           agg->frame_count, txq_id, idx);
+                       IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), "
+                                          "try-count (0x%08x)\n",
+                                          iwl_get_agg_tx_fail_reason(status),
+                                          status & AGG_TX_STATUS_MSK,
+                                          status & AGG_TX_TRY_MSK);
 
                        hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
                        if (!hdr) {
@@ -281,20 +451,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
                }
        } else {
                BUG_ON(txq_id != txq->swq_id);
-
-               info->status.rates[0].count = tx_resp->failure_frame + 1;
-               info->flags |= iwl_tx_status_to_mac80211(status);
-               iwlagn_hwrate_to_tx_control(priv,
-                                       le32_to_cpu(tx_resp->rate_n_flags),
-                                       info);
-
-               IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
-                                  "0x%x retries %d\n",
-                                  txq_id,
-                                  iwl_get_tx_fail_reason(status), status,
-                                  le32_to_cpu(tx_resp->rate_n_flags),
-                                  tx_resp->failure_frame);
-
+               iwlagn_set_tx_status(priv, info, tx_resp, txq_id, false);
                freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
                iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
@@ -1154,7 +1311,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
        return added;
 }
 
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_CMD,
@@ -1162,7 +1319,6 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                .flags = CMD_SIZE_HUGE,
        };
        struct iwl_scan_cmd *scan;
-       struct ieee80211_conf *conf = NULL;
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
        u32 rate_flags = 0;
        u16 cmd_len;
@@ -1175,59 +1331,20 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        int  chan_mod;
        u8 active_chains;
        u8 scan_tx_antennas = priv->hw_params.valid_tx_ant;
+       int ret;
+
+       lockdep_assert_held(&priv->mutex);
 
        if (vif)
                ctx = iwl_rxon_ctx_from_vif(vif);
 
-       conf = ieee80211_get_hw_conf(priv->hw);
-
-       cancel_delayed_work(&priv->scan_check);
-
-       if (!iwl_is_ready(priv)) {
-               IWL_WARN(priv, "request scan called when driver not ready.\n");
-               goto done;
-       }
-
-       /* Make sure the scan wasn't canceled before this queued work
-        * was given the chance to run... */
-       if (!test_bit(STATUS_SCANNING, &priv->status))
-               goto done;
-
-       /* This should never be called or scheduled if there is currently
-        * a scan active in the hardware. */
-       if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-               IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests in parallel. "
-                              "Ignoring second request.\n");
-               goto done;
-       }
-
-       if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Aborting scan due to device shutdown\n");
-               goto done;
-       }
-
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_HC(priv, "Scan request while abort pending.  Queuing.\n");
-               goto done;
-       }
-
-       if (iwl_is_rfkill(priv)) {
-               IWL_DEBUG_HC(priv, "Aborting scan due to RF Kill activation\n");
-               goto done;
-       }
-
-       if (!test_bit(STATUS_READY, &priv->status)) {
-               IWL_DEBUG_HC(priv, "Scan request while uninitialized.  Queuing.\n");
-               goto done;
-       }
-
        if (!priv->scan_cmd) {
                priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) +
                                         IWL_MAX_SCAN_SIZE, GFP_KERNEL);
                if (!priv->scan_cmd) {
                        IWL_DEBUG_SCAN(priv,
                                       "fail to allocate memory for scan\n");
-                       goto done;
+                       return -ENOMEM;
                }
        }
        scan = priv->scan_cmd;
@@ -1334,8 +1451,8 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                                                IWL_GOOD_CRC_TH_NEVER;
                break;
        default:
-               IWL_WARN(priv, "Invalid scan band count\n");
-               goto done;
+               IWL_WARN(priv, "Invalid scan band\n");
+               return -EIO;
        }
 
        band = priv->scan_band;
@@ -1415,7 +1532,7 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        }
        if (scan->channel_count == 0) {
                IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
-               goto done;
+               return -EIO;
        }
 
        cmd.len += le16_to_cpu(scan->tx_cmd.len) +
@@ -1423,30 +1540,21 @@ void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        cmd.data = scan;
        scan->len = cpu_to_le16(cmd.len);
 
-       set_bit(STATUS_SCAN_HW, &priv->status);
-
-       if (priv->cfg->ops->hcmd->set_pan_params &&
-           priv->cfg->ops->hcmd->set_pan_params(priv))
-               goto done;
+       if (priv->cfg->ops->hcmd->set_pan_params) {
+               ret = priv->cfg->ops->hcmd->set_pan_params(priv);
+               if (ret)
+                       return ret;
+       }
 
-       if (iwl_send_cmd_sync(priv, &cmd))
-               goto done;
+       set_bit(STATUS_SCAN_HW, &priv->status);
+       ret = iwl_send_cmd_sync(priv, &cmd);
+       if (ret) {
+               clear_bit(STATUS_SCAN_HW, &priv->status);
+               if (priv->cfg->ops->hcmd->set_pan_params)
+                       priv->cfg->ops->hcmd->set_pan_params(priv);
+       }
 
-       queue_delayed_work(priv->workqueue, &priv->scan_check,
-                          IWL_SCAN_CHECK_WATCHDOG);
-
-       return;
-
- done:
-       /* Cannot perform scan. Make sure we clear scanning
-       * bits from status so next scan request can be performed.
-       * If we don't clear scanning status bit here all next scan
-       * will fail
-       */
-       clear_bit(STATUS_SCAN_HW, &priv->status);
-       clear_bit(STATUS_SCANNING, &priv->status);
-       /* inform mac80211 scan aborted */
-       queue_work(priv->workqueue, &priv->scan_completed);
+       return ret;
 }
 
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
@@ -1673,6 +1781,8 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
        bt_cmd.kill_ack_mask = priv->kill_ack_mask;
        bt_cmd.kill_cts_mask = priv->kill_cts_mask;
        bt_cmd.valid = priv->bt_valid;
+       bt_cmd.tx_prio_boost = 0;
+       bt_cmd.rx_prio_boost = 0;
 
        /*
         * Configure BT coex mode to "no coexistence" when the
index 3970ab1deaf99cbbe8c8437afd91315ac38705fe..357cdb26f16d7aa644ae317999fac4d00ef100f5 100644 (file)
@@ -453,15 +453,6 @@ static inline u8 first_antenna(u8 mask)
 }
 
 
-static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
-{
-       u8 rate = iwl_rates[rate_index].prev_ieee;
-
-       if (rate == IWL_RATE_INVALID)
-               rate = rate_index;
-       return rate;
-}
-
 static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index)
 {
        u8 rate = iwl3945_rates[rate_index].prev_ieee;
index a7961bf395fcfddb1fd5bc9c4bd7a63260e16424..8bfb0495a76b2e17b8a1a4a23aeac05230144f16 100644 (file)
@@ -307,6 +307,17 @@ void iwlagn_init_alive_start(struct iwl_priv *priv)
                goto restart;
        }
 
+       if (priv->cfg->advanced_bt_coexist) {
+               /*
+                * Tell uCode we are ready to perform calibration
+                * need to perform this before any calibration
+                * no need to close the envlope since we are going
+                * to load the runtime uCode later.
+                */
+               iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+                       BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+
+       }
        iwlagn_send_calib_cfg(priv);
        return;
 
@@ -364,7 +375,7 @@ static const u8 iwlagn_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
        0, 0, 0, 0, 0, 0, 0
 };
 
-static void iwlagn_send_prio_tbl(struct iwl_priv *priv)
+void iwlagn_send_prio_tbl(struct iwl_priv *priv)
 {
        struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
 
@@ -375,7 +386,7 @@ static void iwlagn_send_prio_tbl(struct iwl_priv *priv)
                IWL_ERR(priv, "failed to send BT prio tbl command\n");
 }
 
-static void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
+void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
 {
        struct iwl_bt_coex_prot_env_cmd env_cmd;
 
@@ -482,25 +493,6 @@ int iwlagn_alive_notify(struct iwl_priv *priv)
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       if (priv->cfg->advanced_bt_coexist) {
-               /* Configure Bluetooth device coexistence support */
-               /* need to perform this before any calibration */
-               priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
-               priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
-               priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
-               priv->cfg->ops->hcmd->send_bt_config(priv);
-               priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
-
-               if (bt_coex_active && priv->iw_mode != NL80211_IFTYPE_ADHOC) {
-                       iwlagn_send_prio_tbl(priv);
-                       iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
-                               BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-                       iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
-                               BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-               }
-
-       }
-
        iwlagn_send_wimax_coex(priv);
 
        iwlagn_set_Xtal_calib(priv);
index ad0e67f5c0d41d7188ae86f37b78cb5f4263ccda..646864a26eaf5277e6c0d0909c8079d8c23e14cb 100644 (file)
@@ -110,6 +110,9 @@ int iwl_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        if (!iwl_is_alive(priv))
                return -EBUSY;
 
+       if (!ctx->is_active)
+               return 0;
+
        /* always get timestamp with Rx frame */
        ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
 
@@ -223,9 +226,8 @@ int iwl_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                        return ret;
                }
        }
-
-       priv->start_calib = 0;
        if (new_assoc) {
+               priv->start_calib = 0;
                /* Apply the new configuration
                 * RXON assoc doesn't clear the station table in uCode,
                 */
@@ -369,7 +371,7 @@ static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
 
        if (!priv->beacon_ctx) {
                IWL_ERR(priv, "trying to build beacon w/o beacon context!\n");
-               return -EINVAL;
+               return 0;
        }
 
        /* Initialize memory */
@@ -1278,7 +1280,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
                IWL_ERR(priv, "Microcode SW error detected. "
                        " Restarting 0x%X.\n", inta);
                priv->isr_stats.sw++;
-               priv->isr_stats.sw_err = inta;
                iwl_irq_handle_error(priv);
                handled |= CSR_INT_BIT_SW_ERR;
        }
@@ -1459,7 +1460,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
                IWL_ERR(priv, "Microcode SW error detected. "
                        " Restarting 0x%X.\n", inta);
                priv->isr_stats.sw++;
-               priv->isr_stats.sw_err = inta;
                iwl_irq_handle_error(priv);
                handled |= CSR_INT_BIT_SW_ERR;
        }
@@ -2467,6 +2467,7 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
        }
 
        desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32));
+       priv->isr_stats.err_code = desc;
        pc = iwl_read_targ_mem(priv, base + 2 * sizeof(u32));
        blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32));
        blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32));
@@ -2813,6 +2814,22 @@ static void iwl_alive_start(struct iwl_priv *priv)
        if (iwl_is_rfkill(priv))
                return;
 
+       if (priv->cfg->advanced_bt_coexist) {
+               /* Configure Bluetooth device coexistence support */
+               priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
+               priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
+               priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
+               priv->cfg->ops->hcmd->send_bt_config(priv);
+               priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
+               if (bt_coex_active && priv->iw_mode != NL80211_IFTYPE_ADHOC)
+                       iwlagn_send_prio_tbl(priv);
+
+               /* FIXME: w/a to force change uCode BT state machine */
+               iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
+                       BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+               iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
+                       BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       }
        ieee80211_wake_queues(priv->hw);
 
        priv->active_rate = IWL_RATES_MASK;
@@ -2875,8 +2892,9 @@ static void __iwl_down(struct iwl_priv *priv)
 
        IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
 
-       if (!exit_pending)
-               set_bit(STATUS_EXIT_PENDING, &priv->status);
+       iwl_scan_cancel_timeout(priv, 200);
+
+       exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
        /* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
         * to prevent rearm timer */
@@ -3486,15 +3504,6 @@ static void iwl_mac_stop(struct ieee80211_hw *hw)
 
        priv->is_open = 0;
 
-       if (iwl_is_ready_rf(priv) || test_bit(STATUS_SCAN_HW, &priv->status)) {
-               /* stop mac, cancel any scan request and clear
-                * RXON_FILTER_ASSOC_MSK BIT
-                */
-               mutex_lock(&priv->mutex);
-               iwl_scan_cancel_timeout(priv, 100);
-               mutex_unlock(&priv->mutex);
-       }
-
        iwl_down(priv);
 
        flush_workqueue(priv->workqueue);
@@ -4062,13 +4071,15 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
                priv->cfg->ops->lib->cancel_deferred_work(priv);
 
        cancel_delayed_work_sync(&priv->init_alive_start);
-       cancel_delayed_work(&priv->scan_check);
-       cancel_work_sync(&priv->start_internal_scan);
        cancel_delayed_work(&priv->alive_start);
        cancel_work_sync(&priv->run_time_calib_work);
        cancel_work_sync(&priv->beacon_update);
+
+       iwl_cancel_scan_deferred_work(priv);
+
        cancel_work_sync(&priv->bt_full_concurrency);
        cancel_work_sync(&priv->bt_runtime_config);
+
        del_timer_sync(&priv->statistics_periodic);
        del_timer_sync(&priv->ucode_trace);
 }
@@ -4286,6 +4297,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        for (i = 0; i < NUM_IWL_RXON_CTX; i++)
                priv->contexts[i].ctxid = i;
 
+       priv->contexts[IWL_RXON_CTX_BSS].always_active = true;
+       priv->contexts[IWL_RXON_CTX_BSS].is_active = true;
        priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON;
        priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING;
        priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC;
index 7c542a8c8f8131e2ce717a513d04251e640c27a1..a372184ac2105b4761d71514abdcd5076d7839ff 100644 (file)
@@ -134,6 +134,8 @@ void iwlagn_rx_calib_complete(struct iwl_priv *priv,
 void iwlagn_init_alive_start(struct iwl_priv *priv);
 int iwlagn_alive_notify(struct iwl_priv *priv);
 int iwl_verify_ucode(struct iwl_priv *priv);
+void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
+void iwlagn_send_prio_tbl(struct iwl_priv *priv);
 
 /* lib */
 void iwl_check_abort_status(struct iwl_priv *priv,
@@ -217,7 +219,7 @@ void iwl_reply_statistics(struct iwl_priv *priv,
                          struct iwl_rx_mem_buffer *rxb);
 
 /* scan */
-void iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
+int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif);
 
 /* station mgmt */
 int iwlagn_manage_ibss_station(struct iwl_priv *priv,
@@ -236,4 +238,9 @@ void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv);
 void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv);
 void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv);
 
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_agg_tx_fail_reason(u16 status);
+#else
+static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; }
+#endif
 #endif /* __iwl_agn_h__ */
index 3e4ba31b5d59ad0a666ad185d3e4c058e00383a8..27e250c8d4b5540ae6704701d922ef6455331081 100644 (file)
@@ -1820,13 +1820,8 @@ enum {
        TX_STATUS_FAIL_TID_DISABLE = 0x8d,
        TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
        TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f,
-       /* uCode drop due to FW drop request */
-       TX_STATUS_FAIL_FW_DROP = 0x90,
-       /*
-        * uCode drop due to station color mismatch
-        * between tx command and station table
-        */
-       TX_STATUS_FAIL_STA_COLOR_MISMATCH_DROP = 0x91,
+       TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90,
+       TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91,
 };
 
 #define        TX_PACKET_MODE_REGULAR          0x0000
@@ -1868,6 +1863,9 @@ enum {
        AGG_TX_STATE_DELAY_TX_MSK = 0x400
 };
 
+#define AGG_TX_STATUS_MSK      0x00000fff      /* bits 0:11 */
+#define AGG_TX_TRY_MSK         0x0000f000      /* bits 12:15 */
+
 #define AGG_TX_STATE_LAST_SENT_MSK  (AGG_TX_STATE_LAST_SENT_TTL_MSK | \
                                     AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
                                     AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
@@ -2488,7 +2486,12 @@ struct iwlagn_bt_cmd {
        __le16 bt4_decision_time; /* unused */
        __le16 valid;
        u8 prio_boost;
-       u8 reserved[3];
+       /*
+        * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask
+        * if configure the following patterns
+        */
+       u8 tx_prio_boost;       /* SW boost of WiFi tx priority */
+       __le16 rx_prio_boost;   /* SW boost of WiFi rx priority */
 };
 
 #define IWLAGN_BT_SCO_ACTIVE   cpu_to_le32(BIT(0))
index 87a2e40972ba85437a9fd26dd239244b2c1cf1fb..393f02d94c4e324a2439ee387e6117240c9d9e67 100644 (file)
@@ -196,6 +196,9 @@ static void iwl_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        if (test_bit(STATUS_EXIT_PENDING, &priv->status))
                return;
 
+       if (!ctx->is_active)
+               return;
+
        ctx->qos_data.def_qos_parm.qos_flags = 0;
 
        if (ctx->qos_data.qos_active)
@@ -488,8 +491,29 @@ EXPORT_SYMBOL(iwl_is_ht40_tx_allowed);
 
 static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
 {
-       u16 new_val = 0;
-       u16 beacon_factor = 0;
+       u16 new_val;
+       u16 beacon_factor;
+
+       /*
+        * If mac80211 hasn't given us a beacon interval, program
+        * the default into the device (not checking this here
+        * would cause the adjustment below to return the maximum
+        * value, which may break PAN.)
+        */
+       if (!beacon_val)
+               return DEFAULT_BEACON_INTERVAL;
+
+       /*
+        * If the beacon interval we obtained from the peer
+        * is too large, we'll have to wake up more often
+        * (and in IBSS case, we'll beacon too much)
+        *
+        * For example, if max_beacon_val is 4096, and the
+        * requested beacon interval is 7000, we'll have to
+        * use 3500 to be able to wake up on the beacons.
+        *
+        * This could badly influence beacon detection stats.
+        */
 
        beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
        new_val = beacon_val / beacon_factor;
@@ -526,10 +550,22 @@ int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        ctx->timing.atim_window = 0;
 
        if (ctx->ctxid == IWL_RXON_CTX_PAN &&
-           (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION)) {
+           (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
+           iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
+           priv->contexts[IWL_RXON_CTX_BSS].vif &&
+           priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
                ctx->timing.beacon_interval =
                        priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
                beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+       } else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
+                  iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
+                  priv->contexts[IWL_RXON_CTX_PAN].vif &&
+                  priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
+                  (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
+                   !ctx->vif->bss_conf.beacon_int)) {
+               ctx->timing.beacon_interval =
+                       priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
+               beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
        } else {
                beacon_int = iwl_adjust_beacon_interval(beacon_int,
                                priv->hw_params.max_beacon_itrvl * TIME_UNIT);
@@ -1797,9 +1833,8 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
                priv->ibss_beacon = ieee80211_beacon_get(hw, vif);
        }
 
-       if (changes & BSS_CHANGED_BEACON_INT) {
-               /* TODO: in AP mode, do something to make this take effect */
-       }
+       if (changes & BSS_CHANGED_BEACON_INT && vif->type == NL80211_IFTYPE_AP)
+               iwl_send_rxon_timing(priv, ctx);
 
        if (changes & BSS_CHANGED_BSSID) {
                IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid);
@@ -2009,9 +2044,14 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
         */
        priv->iw_mode = vif->type;
 
+       ctx->is_active = true;
+
        err = iwl_set_mode(priv, vif);
-       if (err)
+       if (err) {
+               if (!ctx->always_active)
+                       ctx->is_active = false;
                goto out_err;
+       }
 
        if (priv->cfg->advanced_bt_coexist &&
            vif->type == NL80211_IFTYPE_ADHOC) {
@@ -2041,7 +2081,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
 {
        struct iwl_priv *priv = hw->priv;
        struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-       bool scan_completed = false;
 
        IWL_DEBUG_MAC80211(priv, "enter\n");
 
@@ -2050,14 +2089,14 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
        WARN_ON(ctx->vif != vif);
        ctx->vif = NULL;
 
-       iwl_scan_cancel_timeout(priv, 100);
-       iwl_set_mode(priv, vif);
-
        if (priv->scan_vif == vif) {
-               scan_completed = true;
-               priv->scan_vif = NULL;
-               priv->scan_request = NULL;
+               iwl_scan_cancel_timeout(priv, 200);
+               iwl_force_scan_end(priv);
        }
+       iwl_set_mode(priv, vif);
+
+       if (!ctx->always_active)
+               ctx->is_active = false;
 
        /*
         * When removing the IBSS interface, overwrite the
@@ -2072,9 +2111,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
        memset(priv->bssid, 0, ETH_ALEN);
        mutex_unlock(&priv->mutex);
 
-       if (scan_completed)
-               ieee80211_scan_completed(priv->hw, true);
-
        IWL_DEBUG_MAC80211(priv, "leave\n");
 
 }
@@ -2255,6 +2291,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
+       iwl_scan_cancel_timeout(priv, 100);
        if (!iwl_is_ready_rf(priv)) {
                IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
                mutex_unlock(&priv->mutex);
@@ -2264,7 +2301,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
        /* we are restarting association process
         * clear RXON_FILTER_ASSOC_MSK bit
         */
-       iwl_scan_cancel_timeout(priv, 100);
        ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        iwlcore_commit_rxon(priv, ctx);
 
index f7b57ed84f66610d01e91b131cdae51970d09bed..f0302bfe85f55c46eee85bcbafc48c93270a8c7d 100644 (file)
@@ -111,7 +111,7 @@ struct iwl_hcmd_utils_ops {
                                  __le16 fc, __le32 *tx_flags);
        int  (*calc_rssi)(struct iwl_priv *priv,
                          struct iwl_rx_phy_res *rx_resp);
-       void (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
+       int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif);
 };
 
 struct iwl_apm_ops {
@@ -130,6 +130,8 @@ struct iwl_debugfs_ops {
                                      size_t count, loff_t *ppos);
        ssize_t (*bt_stats_read)(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos);
+       ssize_t (*reply_tx_error)(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos);
 };
 
 struct iwl_temp_ops {
@@ -553,6 +555,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+void iwl_force_scan_end(struct iwl_priv *priv);
 int iwl_mac_hw_scan(struct ieee80211_hw *hw,
                    struct ieee80211_vif *vif,
                    struct cfg80211_scan_request *req);
@@ -568,6 +571,7 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
                               enum ieee80211_band band,
                               struct ieee80211_vif *vif);
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
 
 /* For faster active scanning, scan will move to the next channel if fewer than
  * PLCP_QUIET_THRESH packets are heard on this channel within
index 0ee8f516c4ab0e4b1711f32792ec89d84c52a65f..265ad01a443f9adb887fe7c606098e5bf94f8ecd 100644 (file)
@@ -575,10 +575,10 @@ static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
                priv->isr_stats.hw);
        pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n",
                priv->isr_stats.sw);
-       if (priv->isr_stats.sw > 0) {
+       if (priv->isr_stats.sw || priv->isr_stats.hw) {
                pos += scnprintf(buf + pos, bufsz - pos,
                        "\tLast Restarting Code:  0x%X\n",
-                       priv->isr_stats.sw_err);
+                       priv->isr_stats.err_code);
        }
 #ifdef CONFIG_IWLWIFI_DEBUG
        pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n",
@@ -1604,6 +1604,56 @@ static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file,
        return ret;
 }
 
+static ssize_t iwl_dbgfs_protection_mode_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+
+       int pos = 0;
+       char buf[40];
+       const size_t bufsz = sizeof(buf);
+
+       pos += scnprintf(buf + pos, bufsz - pos, "use %s for aggregation\n",
+                        (priv->cfg->use_rts_for_aggregation) ? "rts/cts" :
+                        "cts-to-self");
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_protection_mode_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos) {
+
+       struct iwl_priv *priv = file->private_data;
+       char buf[8];
+       int buf_size;
+       int rts;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       if (sscanf(buf, "%d", &rts) != 1)
+               return -EINVAL;
+       if (rts)
+               priv->cfg->use_rts_for_aggregation = true;
+       else
+               priv->cfg->use_rts_for_aggregation = false;
+       return count;
+}
+
+static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = file->private_data;
+
+       if (priv->cfg->ops->lib->debugfs_ops.reply_tx_error)
+               return priv->cfg->ops->lib->debugfs_ops.reply_tx_error(
+                       file, user_buf, count, ppos);
+       else
+               return -ENODATA;
+}
 DEBUGFS_READ_FILE_OPS(rx_statistics);
 DEBUGFS_READ_FILE_OPS(tx_statistics);
 DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@@ -1629,6 +1679,8 @@ DEBUGFS_WRITE_FILE_OPS(txfifo_flush);
 DEBUGFS_READ_FILE_OPS(ucode_bt_stats);
 DEBUGFS_WRITE_FILE_OPS(monitor_period);
 DEBUGFS_READ_FILE_OPS(bt_traffic);
+DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
+DEBUGFS_READ_FILE_OPS(reply_tx_error);
 
 /*
  * Create the debugfs files and directories
@@ -1689,6 +1741,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
        DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
        if (priv->cfg->ops->lib->dev_txfifo_flush)
                DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
+       DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR);
 
        if (priv->cfg->sensitivity_calib_by_driver)
                DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);
@@ -1698,6 +1751,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
                DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR);
        if (priv->cfg->bt_statistics)
                DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR);
+       DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR);
        DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR);
        DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
        DEBUGFS_ADD_FILE(monitor_period, dir_debug, S_IWUSR);
index 4dd38b7b8b743a1614678bab5732788c779e46fc..74d25bcbfcb27e7532c9facc8e05600760bc4593 100644 (file)
@@ -945,7 +945,7 @@ enum iwl_pa_type {
 struct isr_statistics {
        u32 hw;
        u32 sw;
-       u32 sw_err;
+       u32 err_code;
        u32 sch;
        u32 alive;
        u32 rfkill;
@@ -957,6 +957,50 @@ struct isr_statistics {
        u32 unhandled;
 };
 
+/* reply_tx_statistics (for _agn devices) */
+struct reply_tx_error_statistics {
+       u32 pp_delay;
+       u32 pp_few_bytes;
+       u32 pp_bt_prio;
+       u32 pp_quiet_period;
+       u32 pp_calc_ttak;
+       u32 int_crossed_retry;
+       u32 short_limit;
+       u32 long_limit;
+       u32 fifo_underrun;
+       u32 drain_flow;
+       u32 rfkill_flush;
+       u32 life_expire;
+       u32 dest_ps;
+       u32 host_abort;
+       u32 bt_retry;
+       u32 sta_invalid;
+       u32 frag_drop;
+       u32 tid_disable;
+       u32 fifo_flush;
+       u32 insuff_cf_poll;
+       u32 fail_hw_drop;
+       u32 sta_color_mismatch;
+       u32 unknown;
+};
+
+/* reply_agg_tx_statistics (for _agn devices) */
+struct reply_agg_tx_error_statistics {
+       u32 underrun;
+       u32 bt_prio;
+       u32 few_bytes;
+       u32 abort;
+       u32 last_sent_ttl;
+       u32 last_sent_try;
+       u32 last_sent_bt_kill;
+       u32 scd_query;
+       u32 bad_crc32;
+       u32 response;
+       u32 dump_tx;
+       u32 delay_tx;
+       u32 unknown;
+};
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 /* management statistics */
 enum iwl_mgmt_stats {
@@ -1116,6 +1160,13 @@ struct iwl_rxon_context {
        const u8 *ac_to_queue;
        u8 mcast_queue;
 
+       /*
+        * We could use the vif to indicate active, but we
+        * also need it to be active during disabling when
+        * we already removed the vif for type setting.
+        */
+       bool always_active, is_active;
+
        enum iwl_rxon_context_id ctxid;
 
        u32 interface_modes, exclusive_interface_modes;
@@ -1408,6 +1459,9 @@ struct iwl_priv {
 
                        struct iwl_notif_statistics statistics;
                        struct iwl_bt_notif_statistics statistics_bt;
+                       /* counts reply_tx error */
+                       struct reply_tx_error_statistics reply_tx_stats;
+                       struct reply_agg_tx_error_statistics reply_agg_tx_stats;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
                        struct iwl_notif_statistics accum_statistics;
                        struct iwl_notif_statistics delta_statistics;
index 7727f0966d3168e971dad3c6ffd03d3e45c54369..c54c20023e7c256bc9a82784561bafa0d05b186c 100644 (file)
 #define IWL_PASSIVE_DWELL_BASE      (100)
 #define IWL_CHANNEL_TUNE_TIME       5
 
+static int iwl_send_scan_abort(struct iwl_priv *priv)
+{
+       int ret;
+       struct iwl_rx_packet *pkt;
+       struct iwl_host_cmd cmd = {
+               .id = REPLY_SCAN_ABORT_CMD,
+               .flags = CMD_WANT_SKB,
+       };
 
+       /* Exit instantly with error when device is not ready
+        * to receive scan abort command or it does not perform
+        * hardware scan currently */
+       if (!test_bit(STATUS_READY, &priv->status) ||
+           !test_bit(STATUS_GEO_CONFIGURED, &priv->status) ||
+           !test_bit(STATUS_SCAN_HW, &priv->status) ||
+           test_bit(STATUS_FW_ERROR, &priv->status) ||
+           test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return -EIO;
 
-/**
- * iwl_scan_cancel - Cancel any currently executing HW scan
- *
- * NOTE: priv->mutex is not required before calling this function
- */
-int iwl_scan_cancel(struct iwl_priv *priv)
+       ret = iwl_send_cmd_sync(priv, &cmd);
+       if (ret)
+               return ret;
+
+       pkt = (struct iwl_rx_packet *)cmd.reply_page;
+       if (pkt->u.status != CAN_ABORT_STATUS) {
+               /* The scan abort will return 1 for success or
+                * 2 for "failure".  A failure condition can be
+                * due to simply not being in an active scan which
+                * can occur if we send the scan abort before we
+                * the microcode has notified us that a scan is
+                * completed. */
+               IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", pkt->u.status);
+               ret = -EIO;
+       }
+
+       iwl_free_pages(priv, cmd.reply_page);
+       return ret;
+}
+
+static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
 {
-       if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
-               clear_bit(STATUS_SCANNING, &priv->status);
-               return 0;
+       /* check if scan was requested from mac80211 */
+       if (priv->scan_request) {
+               IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n");
+               ieee80211_scan_completed(priv->hw, aborted);
        }
 
-       if (test_bit(STATUS_SCANNING, &priv->status)) {
-               if (!test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-                       IWL_DEBUG_SCAN(priv, "Queuing scan abort.\n");
-                       queue_work(priv->workqueue, &priv->abort_scan);
+       priv->is_internal_short_scan = false;
+       priv->scan_vif = NULL;
+       priv->scan_request = NULL;
+}
 
-               } else
-                       IWL_DEBUG_SCAN(priv, "Scan abort already in progress.\n");
+void iwl_force_scan_end(struct iwl_priv *priv)
+{
+       lockdep_assert_held(&priv->mutex);
 
-               return test_bit(STATUS_SCANNING, &priv->status);
+       if (!test_bit(STATUS_SCANNING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n");
+               return;
        }
 
+       IWL_DEBUG_SCAN(priv, "Forcing scan end\n");
+       clear_bit(STATUS_SCANNING, &priv->status);
+       clear_bit(STATUS_SCAN_HW, &priv->status);
+       clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+       iwl_complete_scan(priv, true);
+}
+EXPORT_SYMBOL(iwl_force_scan_end);
+
+static void iwl_do_scan_abort(struct iwl_priv *priv)
+{
+       int ret;
+
+       lockdep_assert_held(&priv->mutex);
+
+       if (!test_bit(STATUS_SCANNING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n");
+               return;
+       }
+
+       if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan abort in progress\n");
+               return;
+       }
+
+       ret = iwl_send_scan_abort(priv);
+       if (ret) {
+               IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret);
+               iwl_force_scan_end(priv);
+       } else
+               IWL_DEBUG_SCAN(priv, "Sucessfully send scan abort\n");
+}
+
+/**
+ * iwl_scan_cancel - Cancel any currently executing HW scan
+ */
+int iwl_scan_cancel(struct iwl_priv *priv)
+{
+       IWL_DEBUG_SCAN(priv, "Queuing abort scan\n");
+       queue_work(priv->workqueue, &priv->abort_scan);
        return 0;
 }
 EXPORT_SYMBOL(iwl_scan_cancel);
+
 /**
  * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
  * @ms: amount of time to wait (in milliseconds) for scan to abort
  *
- * NOTE: priv->mutex must be held before calling this function
  */
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
 {
-       unsigned long now = jiffies;
-       int ret;
-
-       ret = iwl_scan_cancel(priv);
-       if (ret && ms) {
-               mutex_unlock(&priv->mutex);
-               while (!time_after(jiffies, now + msecs_to_jiffies(ms)) &&
-                               test_bit(STATUS_SCANNING, &priv->status))
-                       msleep(1);
-               mutex_lock(&priv->mutex);
-
-               return test_bit(STATUS_SCANNING, &priv->status);
-       }
+       unsigned long timeout = jiffies + msecs_to_jiffies(ms);
 
-       return ret;
-}
-EXPORT_SYMBOL(iwl_scan_cancel_timeout);
+       lockdep_assert_held(&priv->mutex);
 
-static int iwl_send_scan_abort(struct iwl_priv *priv)
-{
-       int ret = 0;
-       struct iwl_rx_packet *pkt;
-       struct iwl_host_cmd cmd = {
-               .id = REPLY_SCAN_ABORT_CMD,
-               .flags = CMD_WANT_SKB,
-       };
+       IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n");
 
-       /* If there isn't a scan actively going on in the hardware
-        * then we are in between scan bands and not actually
-        * actively scanning, so don't send the abort command */
-       if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
-               clear_bit(STATUS_SCAN_ABORTING, &priv->status);
-               return 0;
-       }
-
-       ret = iwl_send_cmd_sync(priv, &cmd);
-       if (ret) {
-               clear_bit(STATUS_SCAN_ABORTING, &priv->status);
-               return ret;
-       }
+       iwl_do_scan_abort(priv);
 
-       pkt = (struct iwl_rx_packet *)cmd.reply_page;
-       if (pkt->u.status != CAN_ABORT_STATUS) {
-               /* The scan abort will return 1 for success or
-                * 2 for "failure".  A failure condition can be
-                * due to simply not being in an active scan which
-                * can occur if we send the scan abort before we
-                * the microcode has notified us that a scan is
-                * completed. */
-               IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", pkt->u.status);
-               clear_bit(STATUS_SCAN_ABORTING, &priv->status);
-               clear_bit(STATUS_SCAN_HW, &priv->status);
+       while (time_before_eq(jiffies, timeout)) {
+               if (!test_bit(STATUS_SCAN_HW, &priv->status))
+                       break;
+               msleep(20);
        }
 
-       iwl_free_pages(priv, cmd.reply_page);
-
-       return ret;
+       return test_bit(STATUS_SCAN_HW, &priv->status);
 }
+EXPORT_SYMBOL(iwl_scan_cancel_timeout);
 
 /* Service response to REPLY_SCAN_CMD (0x80) */
 static void iwl_rx_reply_scan(struct iwl_priv *priv,
@@ -158,7 +192,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
        struct iwl_scanreq_notification *notif =
            (struct iwl_scanreq_notification *)pkt->u.raw;
 
-       IWL_DEBUG_RX(priv, "Scan request status = 0x%x\n", notif->status);
+       IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status);
 #endif
 }
 
@@ -217,26 +251,16 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
        /* The HW is no longer scanning */
        clear_bit(STATUS_SCAN_HW, &priv->status);
 
-       IWL_DEBUG_INFO(priv, "Scan on %sGHz took %dms\n",
+       IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
                       (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
                       jiffies_to_msecs(elapsed_jiffies
                                        (priv->scan_start, jiffies)));
 
-       /*
-        * If a request to abort was given, or the scan did not succeed
-        * then we reset the scan state machine and terminate,
-        * re-queuing another scan if one has been requested
-        */
-       if (test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status))
-               IWL_DEBUG_INFO(priv, "Aborted scan completed.\n");
-
-       IWL_DEBUG_INFO(priv, "Setting scan to off\n");
-
-       clear_bit(STATUS_SCANNING, &priv->status);
+       queue_work(priv->workqueue, &priv->scan_completed);
 
        if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&
-           priv->cfg->advanced_bt_coexist && priv->bt_status !=
-           scan_notif->bt_status) {
+           priv->cfg->advanced_bt_coexist &&
+           priv->bt_status != scan_notif->bt_status) {
                if (scan_notif->bt_status) {
                        /* BT on */
                        if (!priv->bt_ch_announce)
@@ -254,7 +278,6 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
                priv->bt_status = scan_notif->bt_status;
                queue_work(priv->workqueue, &priv->bt_traffic_change_work);
        }
-       queue_work(priv->workqueue, &priv->scan_completed);
 }
 
 void iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
@@ -324,19 +347,53 @@ void iwl_init_scan_params(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_init_scan_params);
 
-static int iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif)
+static int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+                                         struct ieee80211_vif *vif,
+                                         bool internal,
+                                         enum ieee80211_band band)
 {
+       int ret;
+
        lockdep_assert_held(&priv->mutex);
 
-       IWL_DEBUG_INFO(priv, "Starting scan...\n");
+       if (WARN_ON(!priv->cfg->ops->utils->request_scan))
+               return -EOPNOTSUPP;
+
+       cancel_delayed_work(&priv->scan_check);
+
+       if (!iwl_is_ready_rf(priv)) {
+               IWL_WARN(priv, "Request scan called when driver not ready.\n");
+               return -EIO;
+       }
+
+       if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+               IWL_DEBUG_SCAN(priv,
+                       "Multiple concurrent scan requests in parallel.\n");
+               return -EBUSY;
+       }
+
+       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n");
+               return -EBUSY;
+       }
+
+       IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
+                       internal ? "internal short " : "");
+
        set_bit(STATUS_SCANNING, &priv->status);
-       priv->is_internal_short_scan = false;
+       priv->is_internal_short_scan = internal;
        priv->scan_start = jiffies;
+       priv->scan_band = band;
 
-       if (WARN_ON(!priv->cfg->ops->utils->request_scan))
-               return -EOPNOTSUPP;
+       ret = priv->cfg->ops->utils->request_scan(priv, vif);
+       if (ret) {
+               clear_bit(STATUS_SCANNING, &priv->status);
+               priv->is_internal_short_scan = false;
+               return ret;
+       }
 
-       priv->cfg->ops->utils->request_scan(priv, vif);
+       queue_delayed_work(priv->workqueue, &priv->scan_check,
+                          IWL_SCAN_CHECK_WATCHDOG);
 
        return 0;
 }
@@ -355,12 +412,6 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
 
        mutex_lock(&priv->mutex);
 
-       if (!iwl_is_ready_rf(priv)) {
-               ret = -EIO;
-               IWL_DEBUG_MAC80211(priv, "leave - not ready or exit pending\n");
-               goto out_unlock;
-       }
-
        if (test_bit(STATUS_SCANNING, &priv->status) &&
            !priv->is_internal_short_scan) {
                IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
@@ -368,14 +419,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
                goto out_unlock;
        }
 
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
-               ret = -EAGAIN;
-               goto out_unlock;
-       }
-
        /* mac80211 will only ask for one band at a time */
-       priv->scan_band = req->channels[0]->band;
        priv->scan_request = req;
        priv->scan_vif = vif;
 
@@ -383,10 +427,12 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
         * If an internal scan is in progress, just set
         * up the scan_request as per above.
         */
-       if (priv->is_internal_short_scan)
+       if (priv->is_internal_short_scan) {
+               IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n");
                ret = 0;
-       else
-               ret = iwl_scan_initiate(priv, vif);
+       } else
+               ret = iwl_scan_initiate(priv, vif, false,
+                                       req->channels[0]->band);
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
 
@@ -411,6 +457,8 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
        struct iwl_priv *priv =
                container_of(work, struct iwl_priv, start_internal_scan);
 
+       IWL_DEBUG_SCAN(priv, "Start internal scan\n");
+
        mutex_lock(&priv->mutex);
 
        if (priv->is_internal_short_scan == true) {
@@ -418,31 +466,13 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
                goto unlock;
        }
 
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
-               goto unlock;
-       }
-
        if (test_bit(STATUS_SCANNING, &priv->status)) {
                IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
                goto unlock;
        }
 
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
-               goto unlock;
-       }
-
-       priv->scan_band = priv->band;
-
-       IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
-       set_bit(STATUS_SCANNING, &priv->status);
-       priv->is_internal_short_scan = true;
-
-       if (WARN_ON(!priv->cfg->ops->utils->request_scan))
-               goto unlock;
-
-       priv->cfg->ops->utils->request_scan(priv, NULL);
+       if (iwl_scan_initiate(priv, NULL, true, priv->band))
+               IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
  unlock:
        mutex_unlock(&priv->mutex);
 }
@@ -452,18 +482,13 @@ static void iwl_bg_scan_check(struct work_struct *data)
        struct iwl_priv *priv =
            container_of(data, struct iwl_priv, scan_check.work);
 
-       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
-               return;
+       IWL_DEBUG_SCAN(priv, "Scan check work\n");
 
+       /* Since we are here firmware does not finish scan and
+        * most likely is in bad shape, so we don't bother to
+        * send abort command, just force scan complete to mac80211 */
        mutex_lock(&priv->mutex);
-       if (test_bit(STATUS_SCANNING, &priv->status) &&
-           !test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Scan completion watchdog (%dms)\n",
-                              jiffies_to_msecs(IWL_SCAN_CHECK_WATCHDOG));
-
-               if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
-                       iwl_send_scan_abort(priv);
-       }
+       iwl_force_scan_end(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -519,15 +544,12 @@ static void iwl_bg_abort_scan(struct work_struct *work)
 {
        struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
 
-       if (!test_bit(STATUS_READY, &priv->status) ||
-           !test_bit(STATUS_GEO_CONFIGURED, &priv->status))
-               return;
-
-       cancel_delayed_work(&priv->scan_check);
+       IWL_DEBUG_SCAN(priv, "Abort scan work\n");
 
+       /* We keep scan_check work queued in case when firmware will not
+        * report back scan completed notification */
        mutex_lock(&priv->mutex);
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status))
-               iwl_send_scan_abort(priv);
+       iwl_scan_cancel_timeout(priv, 200);
        mutex_unlock(&priv->mutex);
 }
 
@@ -535,30 +557,52 @@ static void iwl_bg_scan_completed(struct work_struct *work)
 {
        struct iwl_priv *priv =
            container_of(work, struct iwl_priv, scan_completed);
-       bool internal = false;
-       bool scan_completed = false;
+       bool aborted;
        struct iwl_rxon_context *ctx;
 
-       IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
+       IWL_DEBUG_SCAN(priv, "Completed %sscan.\n",
+                      priv->is_internal_short_scan ? "internal short " : "");
 
        cancel_delayed_work(&priv->scan_check);
 
        mutex_lock(&priv->mutex);
-       if (priv->is_internal_short_scan) {
-               priv->is_internal_short_scan = false;
-               IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
-               internal = true;
-       } else if (priv->scan_request) {
-               scan_completed = true;
-               priv->scan_request = NULL;
-               priv->scan_vif = NULL;
+
+       aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status);
+       if (aborted)
+               IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n");
+
+       if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan already completed.\n");
+               goto out_settings;
        }
 
-       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+       if (priv->is_internal_short_scan && !aborted) {
+               int err;
+
+               /* Check if mac80211 requested scan during our internal scan */
+               if (priv->scan_request == NULL)
+                       goto out_complete;
+
+               /* If so request a new scan */
+               err = iwl_scan_initiate(priv, priv->scan_vif, false,
+                                       priv->scan_request->channels[0]->band);
+               if (err) {
+                       IWL_DEBUG_SCAN(priv,
+                               "failed to initiate pending scan: %d\n", err);
+                       aborted = true;
+                       goto out_complete;
+               }
+
                goto out;
+       }
+
+out_complete:
+       iwl_complete_scan(priv, aborted);
 
-       if (internal && priv->scan_request)
-               iwl_scan_initiate(priv, priv->scan_vif);
+out_settings:
+       /* Can we still talk to firmware ? */
+       if (!iwl_is_ready_rf(priv))
+               goto out;
 
        /* Since setting the TXPOWER may have been deferred while
         * performing the scan, fire one off */
@@ -571,19 +615,11 @@ static void iwl_bg_scan_completed(struct work_struct *work)
        for_each_context(priv, ctx)
                iwlcore_commit_rxon(priv, ctx);
 
- out:
        if (priv->cfg->ops->hcmd->set_pan_params)
                priv->cfg->ops->hcmd->set_pan_params(priv);
 
+ out:
        mutex_unlock(&priv->mutex);
-
-       /*
-        * Do not hold mutex here since this will cause mac80211 to call
-        * into driver again into functions that will attempt to take
-        * mutex.
-        */
-       if (scan_completed)
-               ieee80211_scan_completed(priv->hw, false);
 }
 
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
@@ -595,3 +631,16 @@ void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_setup_scan_deferred_work);
 
+void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
+{
+       cancel_work_sync(&priv->start_internal_scan);
+       cancel_work_sync(&priv->abort_scan);
+       cancel_work_sync(&priv->scan_completed);
+
+       if (cancel_delayed_work_sync(&priv->scan_check)) {
+               mutex_lock(&priv->mutex);
+               iwl_force_scan_end(priv);
+               mutex_unlock(&priv->mutex);
+       }
+}
+EXPORT_SYMBOL(iwl_cancel_scan_deferred_work);
index ccd09027c7cdd8f3a1b479e6fda1215c960bd102..6edd0341dfe2b1572ab0e888c482e3855e8ee8d0 100644 (file)
@@ -386,7 +386,8 @@ static struct iwl_link_quality_cmd *iwl_sta_alloc_lq(struct iwl_priv *priv,
 {
        int i, r;
        struct iwl_link_quality_cmd *link_cmd;
-       u32 rate_flags;
+       u32 rate_flags = 0;
+       __le32 rate_n_flags;
 
        link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL);
        if (!link_cmd) {
@@ -400,18 +401,14 @@ static struct iwl_link_quality_cmd *iwl_sta_alloc_lq(struct iwl_priv *priv,
        else
                r = IWL_RATE_1M_INDEX;
 
-       for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-               rate_flags = 0;
-               if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
-                       rate_flags |= RATE_MCS_CCK_MSK;
+       if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
+               rate_flags |= RATE_MCS_CCK_MSK;
 
-               rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+       rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
                                RATE_MCS_ANT_POS;
-
-               link_cmd->rs_table[i].rate_n_flags =
-                       iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
-               r = iwl_get_prev_ieee_rate(r);
-       }
+       rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
+       for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
+               link_cmd->rs_table[i].rate_n_flags = rate_n_flags;
 
        link_cmd->general_params.single_stream_ant_msk =
                                first_antenna(priv->hw_params.valid_tx_ant);
index 347d3dc6a01549632227ae8f1acd5ae84a1c50e0..3290b1552f5a8dd9f035ff5b09af51348369fed2 100644 (file)
@@ -666,8 +666,8 @@ const char *iwl_get_tx_fail_reason(u32 status)
                TX_STATUS_FAIL(TID_DISABLE);
                TX_STATUS_FAIL(FIFO_FLUSHED);
                TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
-               TX_STATUS_FAIL(FW_DROP);
-               TX_STATUS_FAIL(STA_COLOR_MISMATCH_DROP);
+               TX_STATUS_FAIL(PASSIVE_NO_RX);
+               TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
        }
 
        return "UNKNOWN";
index 68e624afb9879d28554e522e8857137aea5ff212..116777122a798b2b700f8aa86cce11112a956a9f 100644 (file)
@@ -1730,7 +1730,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
                IWL_ERR(priv, "Microcode SW error detected. "
                        "Restarting 0x%X.\n", inta);
                priv->isr_stats.sw++;
-               priv->isr_stats.sw_err = inta;
                iwl_irq_handle_error(priv);
                handled |= CSR_INT_BIT_SW_ERR;
        }
@@ -2568,15 +2567,13 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv);
 static void __iwl3945_down(struct iwl_priv *priv)
 {
        unsigned long flags;
-       int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status);
-       struct ieee80211_conf *conf = NULL;
+       int exit_pending;
 
        IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n");
 
-       conf = ieee80211_get_hw_conf(priv->hw);
+       iwl_scan_cancel_timeout(priv, 200);
 
-       if (!exit_pending)
-               set_bit(STATUS_EXIT_PENDING, &priv->status);
+       exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
        /* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
         * to prevent rearm timer */
@@ -2820,7 +2817,7 @@ static void iwl3945_rfkill_poll(struct work_struct *data)
 
 }
 
-void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
+int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_CMD,
@@ -2828,61 +2825,19 @@ void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                .flags = CMD_SIZE_HUGE,
        };
        struct iwl3945_scan_cmd *scan;
-       struct ieee80211_conf *conf = NULL;
        u8 n_probes = 0;
        enum ieee80211_band band;
        bool is_active = false;
+       int ret;
 
-       conf = ieee80211_get_hw_conf(priv->hw);
-
-       cancel_delayed_work(&priv->scan_check);
-
-       if (!iwl_is_ready(priv)) {
-               IWL_WARN(priv, "request scan called when driver not ready.\n");
-               goto done;
-       }
-
-       /* Make sure the scan wasn't canceled before this queued work
-        * was given the chance to run... */
-       if (!test_bit(STATUS_SCANNING, &priv->status))
-               goto done;
-
-       /* This should never be called or scheduled if there is currently
-        * a scan active in the hardware. */
-       if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-               IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests  "
-                               "Ignoring second request.\n");
-               goto done;
-       }
-
-       if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Aborting scan due to device shutdown\n");
-               goto done;
-       }
-
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_HC(priv,
-                       "Scan request while abort pending. Queuing.\n");
-               goto done;
-       }
-
-       if (iwl_is_rfkill(priv)) {
-               IWL_DEBUG_HC(priv, "Aborting scan due to RF Kill activation\n");
-               goto done;
-       }
-
-       if (!test_bit(STATUS_READY, &priv->status)) {
-               IWL_DEBUG_HC(priv,
-                       "Scan request while uninitialized. Queuing.\n");
-               goto done;
-       }
+       lockdep_assert_held(&priv->mutex);
 
        if (!priv->scan_cmd) {
                priv->scan_cmd = kmalloc(sizeof(struct iwl3945_scan_cmd) +
                                         IWL_MAX_SCAN_SIZE, GFP_KERNEL);
                if (!priv->scan_cmd) {
                        IWL_DEBUG_SCAN(priv, "Fail to allocate scan memory\n");
-                       goto done;
+                       return -ENOMEM;
                }
        }
        scan = priv->scan_cmd;
@@ -2977,7 +2932,7 @@ void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                break;
        default:
                IWL_WARN(priv, "Invalid scan band\n");
-               goto done;
+               return -EIO;
        }
 
        if (!priv->is_internal_short_scan) {
@@ -3012,7 +2967,7 @@ void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
        if (scan->channel_count == 0) {
                IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
-               goto done;
+               return -EIO;
        }
 
        cmd.len += le16_to_cpu(scan->tx_cmd.len) +
@@ -3021,25 +2976,10 @@ void iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        scan->len = cpu_to_le16(cmd.len);
 
        set_bit(STATUS_SCAN_HW, &priv->status);
-       if (iwl_send_cmd_sync(priv, &cmd))
-               goto done;
-
-       queue_delayed_work(priv->workqueue, &priv->scan_check,
-                          IWL_SCAN_CHECK_WATCHDOG);
-
-       return;
-
- done:
-       /* can not perform scan make sure we clear scanning
-        * bits from status so next scan request can be performed.
-        * if we dont clear scanning status bit here all next scan
-        * will fail
-       */
-       clear_bit(STATUS_SCAN_HW, &priv->status);
-       clear_bit(STATUS_SCANNING, &priv->status);
-
-       /* inform mac80211 scan aborted */
-       queue_work(priv->workqueue, &priv->scan_completed);
+       ret = iwl_send_cmd_sync(priv, &cmd);
+       if (ret)
+               clear_bit(STATUS_SCAN_HW, &priv->status);
+       return ret;
 }
 
 static void iwl3945_bg_restart(struct work_struct *data)
@@ -3233,15 +3173,6 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw)
 
        priv->is_open = 0;
 
-       if (iwl_is_ready_rf(priv)) {
-               /* stop mac, cancel any scan request and clear
-                * RXON_FILTER_ASSOC_MSK BIT
-                */
-               mutex_lock(&priv->mutex);
-               iwl_scan_cancel_timeout(priv, 100);
-               mutex_unlock(&priv->mutex);
-       }
-
        iwl3945_down(priv);
 
        flush_workqueue(priv->workqueue);
@@ -3831,10 +3762,10 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv)
        iwl3945_hw_cancel_deferred_work(priv);
 
        cancel_delayed_work_sync(&priv->init_alive_start);
-       cancel_delayed_work(&priv->scan_check);
        cancel_delayed_work(&priv->alive_start);
-       cancel_work_sync(&priv->start_internal_scan);
        cancel_work_sync(&priv->beacon_update);
+
+       iwl_cancel_scan_deferred_work(priv);
 }
 
 static struct attribute *iwl3945_sysfs_entries[] = {
index 317f086ced0a50cdcf8ac2695e112d9f6886585c..1bbdb14f7d760a9309ac2fafc98b862e1dd0b990 100644 (file)
@@ -481,7 +481,6 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
        struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
        int bsssize;
        const u8 *pos;
-       u16 nr_sets;
        const u8 *tsfdesc;
        int tsfsize;
        int i;
@@ -490,12 +489,11 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
        lbs_deb_enter(LBS_DEB_CFG80211);
 
        bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
-       nr_sets = le16_to_cpu(scanresp->nr_sets);
 
        lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
-                       nr_sets, bsssize, le16_to_cpu(resp->size));
+                       scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
 
-       if (nr_sets == 0) {
+       if (scanresp->nr_sets == 0) {
                ret = 0;
                goto done;
        }
index 194762ab014250feb61b710f66b6fda737b08f2b..acf3bf63ee338286a3af92225cdd0849a4828ae0 100644 (file)
@@ -574,7 +574,7 @@ int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted)
        memset(&cmd, 0, sizeof(cmd));
        cmd.hdr.size = cpu_to_le16(sizeof(cmd));
        cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
-       cmd.id = !!inverted;
+       cmd.id = cpu_to_le32(!!inverted);
 
        ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
 
index 92b486d46eb9fca3d7e53fa39102574156e4d24c..7eaaa3bab54780a334bcd70c99fb2d42b5862b92 100644 (file)
@@ -595,7 +595,8 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif)
 {
        wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n",
-                   __func__, vif->type, vif->addr);
+                   __func__, ieee80211_vif_type_p2p(vif),
+                   vif->addr);
        hwsim_set_magic(vif);
        return 0;
 }
@@ -603,11 +604,14 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
 
 static int mac80211_hwsim_change_interface(struct ieee80211_hw *hw,
                                           struct ieee80211_vif *vif,
-                                          enum nl80211_iftype newtype)
+                                          enum nl80211_iftype newtype,
+                                          bool newp2p)
 {
+       newtype = ieee80211_iftype_p2p(newtype, newp2p);
        wiphy_debug(hw->wiphy,
                    "%s (old type=%d, new type=%d, mac_addr=%pM)\n",
-                   __func__, vif->type, newtype, vif->addr);
+                   __func__, ieee80211_vif_type_p2p(vif),
+                   newtype, vif->addr);
        hwsim_check_magic(vif);
 
        return 0;
@@ -617,7 +621,8 @@ static void mac80211_hwsim_remove_interface(
        struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        wiphy_debug(hw->wiphy, "%s (type=%d mac_addr=%pM)\n",
-                   __func__, vif->type, vif->addr);
+                   __func__, ieee80211_vif_type_p2p(vif),
+                   vif->addr);
        hwsim_check_magic(vif);
        hwsim_clear_magic(vif);
 }
@@ -1310,6 +1315,8 @@ static int __init init_mac80211_hwsim(void)
                hw->wiphy->interface_modes =
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_P2P_GO) |
                        BIT(NL80211_IFTYPE_ADHOC) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
 
index 156e57dbd2cfa858df969ab3e98c8476f173ee89..18d24b7b1e3475c7f42c70204fa6d45105e07b81 100644 (file)
@@ -202,6 +202,8 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
                dev_info(&priv->spi->dev, "loading default eeprom...\n");
                ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom,
                                       sizeof(p54spi_eeprom));
+#else
+               dev_err(&priv->spi->dev, "Failed to request user eeprom\n");
 #endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
        } else {
                dev_info(&priv->spi->dev, "loading user eeprom...\n");
index d91a831a7700b0509f40e4ff1b445a7b9923abac..5ca624a64c42719d21a5ad73b0b04598b2935a97 100644 (file)
@@ -194,7 +194,7 @@ module_param(bc, int, 0);
 module_param(phy_addr, charp, 0);
 module_param(ray_mem_speed, int, 0);
 
-static UCHAR b5_default_startup_parms[] = {
+static const UCHAR b5_default_startup_parms[] = {
        0, 0,                   /* Adhoc station */
        'L', 'I', 'N', 'U', 'X', 0, 0, 0,       /* 32 char ESSID */
        0, 0, 0, 0, 0, 0, 0, 0,
@@ -229,7 +229,7 @@ static UCHAR b5_default_startup_parms[] = {
        2, 0, 0, 0, 0, 0, 0, 0  /* basic rate set */
 };
 
-static UCHAR b4_default_startup_parms[] = {
+static const UCHAR b4_default_startup_parms[] = {
        0, 0,                   /* Adhoc station */
        'L', 'I', 'N', 'U', 'X', 0, 0, 0,       /* 32 char ESSID */
        0, 0, 0, 0, 0, 0, 0, 0,
@@ -261,9 +261,9 @@ static UCHAR b4_default_startup_parms[] = {
 };
 
 /*===========================================================================*/
-static unsigned char eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
+static const u8 eth2_llc[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
 
-static char hop_pattern_length[] = { 1,
+static const char hop_pattern_length[] = { 1,
        USA_HOP_MOD, EUROPE_HOP_MOD,
        JAPAN_HOP_MOD, KOREA_HOP_MOD,
        SPAIN_HOP_MOD, FRANCE_HOP_MOD,
@@ -271,7 +271,7 @@ static char hop_pattern_length[] = { 1,
        JAPAN_TEST_HOP_MOD
 };
 
-static char rcsid[] =
+static const char rcsid[] =
     "Raylink/WebGear wireless LAN - Corey <Thomas corey@world.std.com>";
 
 static const struct net_device_ops ray_netdev_ops = {
@@ -2575,7 +2575,7 @@ static void clear_interrupt(ray_dev_t *local)
 #ifdef CONFIG_PROC_FS
 #define MAXDATA (PAGE_SIZE - 80)
 
-static char *card_status[] = {
+static const char *card_status[] = {
        "Card inserted - uninitialized",        /* 0 */
        "Card not downloaded",                  /* 1 */
        "Waiting for download parameters",      /* 2 */
@@ -2592,8 +2592,8 @@ static char *card_status[] = {
        "Association failed"                    /* 16 */
 };
 
-static char *nettype[] = { "Adhoc", "Infra " };
-static char *framing[] = { "Encapsulation", "Translation" }
+static const char *nettype[] = { "Adhoc", "Infra " };
+static const char *framing[] = { "Encapsulation", "Translation" }
 
 ;
 /*===========================================================================*/
index 103c71164f109d05f7b87fc77b479ae856f7502d..d49e830fa1da8fde6730ef75858f4594d05e6d24 100644 (file)
@@ -321,7 +321,8 @@ static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev,
 }
 
 static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
-                                struct rt2x00lib_erp *erp)
+                                struct rt2x00lib_erp *erp,
+                                u32 changed)
 {
        int preamble_mask;
        u32 reg;
@@ -329,59 +330,72 @@ static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
        /*
         * When short preamble is enabled, we should set bit 0x08
         */
-       preamble_mask = erp->short_preamble << 3;
-
-       rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-       rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x1ff);
-       rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0x13a);
-       rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-       rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-       rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
-       rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
-       rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
-       rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
-       rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
-       rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
-       rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
-       rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
-       rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
-       rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
-
-       rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               preamble_mask = erp->short_preamble << 3;
+
+               rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
+               rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x1ff);
+               rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0x13a);
+               rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+               rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
+               rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
+               rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
+               rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 10));
+               rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
+               rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 20));
+               rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
+               rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 55));
+               rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
+               rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 110));
+               rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+       }
 
-       rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
-       rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
-       rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
 
-       rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-       rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
-       rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
-       rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+               rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+               rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
-       rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
-       rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
-       rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
-       rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+               rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+               rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+               rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+               rt2x00pci_register_write(rt2x00dev, CSR18, reg);
 
-       rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
-       rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
-       rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
-       rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+               rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+               rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+               rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+               rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+               rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
+                                  erp->beacon_int * 16);
+               rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+       }
 }
 
 static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev,
index ab0507110e423b2cd2afbd79b50c1277f29a9661..2214c32317273058c14613e1a3926c78790c5882 100644 (file)
@@ -327,7 +327,8 @@ static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev,
 }
 
 static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
-                                struct rt2x00lib_erp *erp)
+                                struct rt2x00lib_erp *erp,
+                                u32 changed)
 {
        int preamble_mask;
        u32 reg;
@@ -335,59 +336,73 @@ static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
        /*
         * When short preamble is enabled, we should set bit 0x08
         */
-       preamble_mask = erp->short_preamble << 3;
-
-       rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-       rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x162);
-       rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0xa2);
-       rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-       rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-       rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
-       rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
-       rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
-       rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
-       rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
-       rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
-       rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
-       rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
-
-       rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
-       rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
-       rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
-       rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
-       rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
-
-       rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               preamble_mask = erp->short_preamble << 3;
+
+               rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
+               rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, 0x162);
+               rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME, 0xa2);
+               rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+               rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
+               rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
+               rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
+               rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 10));
+               rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
+               rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 20));
+               rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
+               rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 55));
+               rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
+
+               rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
+               rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
+               rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
+               rt2x00_set_field32(&reg, ARCSR2_LENGTH,
+                                  GET_DURATION(ACK_SIZE, 110));
+               rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+       }
 
-       rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
-       rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
-       rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+               rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+               rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
-       rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-       rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
-       rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
-       rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+               rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+               rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+               rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+               rt2x00pci_register_write(rt2x00dev, CSR18, reg);
 
-       rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
-       rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
-       rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
-       rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+               rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+               rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+               rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+               rt2x00pci_register_write(rt2x00dev, CSR19, reg);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+               rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
+                                  erp->beacon_int * 16);
+               rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+       }
 
-       rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
-       rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
-       rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
-       rt2x00pci_register_write(rt2x00dev, CSR19, reg);
 }
 
 static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev,
index db64df4267d8b77a7619374a341c40178f6f72fc..6e94356265b38335dbc8742b0b5d360740724324 100644 (file)
@@ -494,24 +494,34 @@ static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,
 }
 
 static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
-                                struct rt2x00lib_erp *erp)
+                                struct rt2x00lib_erp *erp,
+                                u32 changed)
 {
        u16 reg;
 
-       rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
-       rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
-                          !!erp->short_preamble);
-       rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rt2500usb_register_read(rt2x00dev, TXRX_CSR10, &reg);
+               rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
+                                  !!erp->short_preamble);
+               rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
+       }
 
-       rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates);
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               rt2500usb_register_write(rt2x00dev, TXRX_CSR11,
+                                        erp->basic_rates);
 
-       rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
-       rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL, erp->beacon_int * 4);
-       rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
+               rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
+                                  erp->beacon_int * 4);
+               rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+       }
 
-       rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
-       rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
-       rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
+               rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
+               rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
+       }
 }
 
 static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
index 70a5cb86405b14b0589199700f137e43df771953..2edc7742a7e906f3ab7673813d30b3759282e9dc 100644 (file)
 #define LED_CFG_Y_LED_MODE             FIELD32(0x30000000)
 #define LED_CFG_LED_POLAR              FIELD32(0x40000000)
 
+/*
+ * AMPDU_BA_WINSIZE: Force BlockAck window size
+ * FORCE_WINSIZE_ENABLE:
+ *   0: Disable forcing of BlockAck window size
+ *   1: Enable forcing of BlockAck window size, overwrites values BlockAck
+ *      window size values in the TXWI
+ * FORCE_WINSIZE: BlockAck window size
+ */
+#define AMPDU_BA_WINSIZE               0x1040
+#define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020)
+#define AMPDU_BA_WINSIZE_FORCE_WINSIZE FIELD32(0x0000001f)
+
 /*
  * XIFS_TIME_CFG: MAC timing
  * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX
index 27a6e225083c94211f3a7109e595df74cfac9d4a..3bb67492d754897e663f532fc0986fcded430209 100644 (file)
@@ -1159,38 +1159,50 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
 }
 EXPORT_SYMBOL_GPL(rt2800_config_intf);
 
-void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp)
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
+                      u32 changed)
 {
        u32 reg;
 
-       rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
-       rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
-                          !!erp->short_preamble);
-       rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
-                          !!erp->short_preamble);
-       rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
+               rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
+                                  !!erp->short_preamble);
+               rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
+                                  !!erp->short_preamble);
+               rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+       }
 
-       rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
-       rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
-                          erp->cts_protection ? 2 : 0);
-       rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+               rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+               rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
+                                  erp->cts_protection ? 2 : 0);
+               rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+       }
 
-       rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
-                                erp->basic_rates);
-       rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
+                                        erp->basic_rates);
+               rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+       }
 
-       rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
-       rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time);
-       rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
+               rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME,
+                                  erp->slot_time);
+               rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
 
-       rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
-       rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
-       rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+               rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
+               rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+       }
 
-       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                          erp->beacon_int * 16);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       }
 }
 EXPORT_SYMBOL_GPL(rt2800_config_erp);
 
@@ -2052,6 +2064,14 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS3FBK, 2);
        rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg);
 
+       /*
+        * Do not force the BA window size, we use the TXWI to set it
+        */
+       rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, &reg);
+       rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0);
+       rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0);
+       rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg);
+
        /*
         * We must clear the error counters.
         * These registers are cleared on read,
index 986229c06c19ba02e0c6d9dc7633a7fa8f148113..600c5eb25c41914029a778ec63b95e135d5a143a 100644 (file)
@@ -169,7 +169,8 @@ void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
                          const unsigned int filter_flags);
 void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
                        struct rt2x00intf_conf *conf, const unsigned int flags);
-void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp);
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
+                      u32 changed);
 void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant);
 void rt2800_config(struct rt2x00_dev *rt2x00dev,
                   struct rt2x00lib_conf *libconf,
index 2bcb1507e3ac57289e536a8646f34b24b7818eec..005ee153e0ccf244296ca60f5d02ef1ec32951d5 100644 (file)
@@ -342,24 +342,24 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
        }
 
        rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, mask);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, 0);
        rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_AC0_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_AC1_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_AC2_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_AC3_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_HCCA_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_MGMT_DMA_DONE, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_MCU_COMMAND, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_RXTX_COHERENT, mask);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_AC0_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_AC1_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_AC2_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_AC3_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_HCCA_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_MGMT_DMA_DONE, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_MCU_COMMAND, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_RXTX_COHERENT, 0);
        rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_GPTIMER, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, mask);
-       rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, mask);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_GPTIMER, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, 0);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, 0);
        rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
 }
 
index 0ae942cb66df4d55c5ab3d056f975749a670bd70..7832a5996a8c94b53d38ae70922025081f43e0c0 100644 (file)
@@ -596,7 +596,8 @@ struct rt2x00lib_ops {
 #define CONFIG_UPDATE_BSSID            ( 1 << 3 )
 
        void (*config_erp) (struct rt2x00_dev *rt2x00dev,
-                           struct rt2x00lib_erp *erp);
+                           struct rt2x00lib_erp *erp,
+                           u32 changed);
        void (*config_ant) (struct rt2x00_dev *rt2x00dev,
                            struct antenna_setup *ant);
        void (*config) (struct rt2x00_dev *rt2x00dev,
index 34f34fa7f53a60ec04db9b9bed0a22e23d28ed4c..4c7ff765a8bf5f2ba80dbcd992d6d6327ad7de19 100644 (file)
@@ -81,7 +81,8 @@ void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,
 
 void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
                          struct rt2x00_intf *intf,
-                         struct ieee80211_bss_conf *bss_conf)
+                         struct ieee80211_bss_conf *bss_conf,
+                         u32 changed)
 {
        struct rt2x00lib_erp erp;
 
@@ -102,7 +103,7 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
        /* Update global beacon interval time, this is needed for PS support */
        rt2x00dev->beacon_int = bss_conf->beacon_int;
 
-       rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp);
+       rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
 }
 
 static inline
@@ -129,12 +130,12 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
         */
        if (!(ant->flags & ANTENNA_RX_DIVERSITY))
                config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx);
-       else
+       else if(config.rx == ANTENNA_SW_DIVERSITY)
                config.rx = active->rx;
 
        if (!(ant->flags & ANTENNA_TX_DIVERSITY))
                config.tx = rt2x00lib_config_antenna_check(config.tx, def->tx);
-       else
+       else if (config.tx == ANTENNA_SW_DIVERSITY)
                config.tx = active->tx;
 
        /*
index dc5c6574aaf4f13efb6f43213da5fce85d8040be..70c85ac2e53ec15e2ec200581ea210db306a6159 100644 (file)
@@ -86,7 +86,8 @@ void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,
                           const u8 *mac, const u8 *bssid);
 void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
                          struct rt2x00_intf *intf,
-                         struct ieee80211_bss_conf *conf);
+                         struct ieee80211_bss_conf *conf,
+                         u32 changed);
 void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
                              struct antenna_setup ant);
 void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
index 666cef3f8472e3da35bfd98d6dd74eba1d8d162e..4d534e9dc6286a7d6ff06d5d7874037f72aeb743 100644 (file)
@@ -188,7 +188,6 @@ static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev)
 static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
 {
        struct link_ant *ant = &rt2x00dev->link.ant;
-       unsigned int flags = ant->flags;
 
        /*
         * Determine if software diversity is enabled for
@@ -196,13 +195,13 @@ static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
         * Always perform this check since within the link
         * tuner interval the configuration might have changed.
         */
-       flags &= ~ANTENNA_RX_DIVERSITY;
-       flags &= ~ANTENNA_TX_DIVERSITY;
+       ant->flags &= ~ANTENNA_RX_DIVERSITY;
+       ant->flags &= ~ANTENNA_TX_DIVERSITY;
 
        if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
-               flags |= ANTENNA_RX_DIVERSITY;
+               ant->flags |= ANTENNA_RX_DIVERSITY;
        if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
-               flags |= ANTENNA_TX_DIVERSITY;
+               ant->flags |= ANTENNA_TX_DIVERSITY;
 
        if (!(ant->flags & ANTENNA_RX_DIVERSITY) &&
            !(ant->flags & ANTENNA_TX_DIVERSITY)) {
@@ -210,9 +209,6 @@ static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev)
                return true;
        }
 
-       /* Update flags */
-       ant->flags = flags;
-
        /*
         * If we have only sampled the data over the last period
         * we should now harvest the data. Otherwise just evaluate
index 235e037e65092045d07db64893b7cbdaa29b5785..7862a840984a8a0229cdc1a4c716fd5f41bdc81d 100644 (file)
@@ -669,8 +669,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
         * When the erp information has changed, we should perform
         * additional configuration steps. For all other changes we are done.
         */
-       if (changes & ~(BSS_CHANGED_ASSOC | BSS_CHANGED_HT))
-               rt2x00lib_config_erp(rt2x00dev, intf, bss_conf);
+       if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE |
+                      BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES |
+                      BSS_CHANGED_BEACON_INT))
+               rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes);
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed);
 
index 3a7759929190b78674aed60783fb5b6793124f4c..97b3935f615bfd4388a80cec84ff8bda850c220a 100644 (file)
@@ -594,7 +594,8 @@ static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev,
 }
 
 static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev,
-                              struct rt2x00lib_erp *erp)
+                              struct rt2x00lib_erp *erp,
+                              u32 changed)
 {
        u32 reg;
 
@@ -603,28 +604,36 @@ static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
        rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
 
-       rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
-       rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-       rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
-                          !!erp->short_preamble);
-       rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
+               rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
+               rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
+                                  !!erp->short_preamble);
+               rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
+       }
 
-       rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               rt2x00pci_register_write(rt2x00dev, TXRX_CSR5,
+                                        erp->basic_rates);
 
-       rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-                          erp->beacon_int * 16);
-       rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
+               rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+       }
 
-       rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
-       rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
-       rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
+               rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+               rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
 
-       rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
-       rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
-       rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
-       rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
-       rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
+               rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
+               rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+               rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+               rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+               rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
+       }
 }
 
 static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
@@ -1645,6 +1654,7 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
        rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, &reg);
        rt2x00_set_field32(&reg, INT_MASK_CSR_TXDONE, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_RXDONE, mask);
+       rt2x00_set_field32(&reg, INT_MASK_CSR_BEACON_DONE, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_ENABLE_MITIGATION, mask);
        rt2x00_set_field32(&reg, INT_MASK_CSR_MITIGATION_PERIOD, 0xff);
        rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg);
@@ -1658,6 +1668,7 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_5, mask);
        rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_6, mask);
        rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_7, mask);
+       rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_TWAKEUP, mask);
        rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg);
 }
 
@@ -2106,7 +2117,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
                                "TX status report missed for entry %d\n",
                                entry_done->entry_idx);
 
-                       rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+                       rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN);
                        entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
                }
 
index 87fb2201537b68d96fd2882dfc230129e98ed074..e22f01c1818e141a5802b29b14a4f15e13d71b78 100644 (file)
@@ -545,7 +545,8 @@ static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev,
 }
 
 static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev,
-                              struct rt2x00lib_erp *erp)
+                              struct rt2x00lib_erp *erp,
+                              u32 changed)
 {
        u32 reg;
 
@@ -554,28 +555,36 @@ static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
        rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
 
-       rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
-       rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-       rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
-                          !!erp->short_preamble);
-       rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+               rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
+               rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
+                                  !!erp->short_preamble);
+               rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+       }
 
-       rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
+       if (changed & BSS_CHANGED_BASIC_RATES)
+               rt2x00usb_register_write(rt2x00dev, TXRX_CSR5,
+                                        erp->basic_rates);
 
-       rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-                          erp->beacon_int * 16);
-       rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+               rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+       }
 
-       rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
-       rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
-       rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
+               rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+               rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
 
-       rt2x00usb_register_read(rt2x00dev, MAC_CSR8, &reg);
-       rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
-       rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
-       rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
-       rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg);
+               rt2x00usb_register_read(rt2x00dev, MAC_CSR8, &reg);
+               rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+               rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+               rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+               rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg);
+       }
 }
 
 static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
index 2f98058be4510f217f17c5f09a0f832d5c5e8198..4a8bb25c1739d610295dfeeefe119ae06ca5ee45 100644 (file)
@@ -74,4 +74,7 @@ config WL1271_SDIO
          If you choose to build a module, it'll be called
          wl1271_sdio. Say N if unsure.
 
-
+config WL12XX_PLATFORM_DATA
+       bool
+       depends on WL1271_SDIO != n
+       default y
index c0b68b0a9aa894e0f7e4364151313aa175973919..74ba9ced5393fd35901643a53a5607ec68bf4747 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/platform_device.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/irq.h>
 
 #include "wl1251.h"
index 334ded9881c02ebdb680e4a5d0b2d6d5f2a29ffa..320de79667a6be19caa8f0127f882d84e01dcbeb 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/slab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 
 #include "wl1251.h"
 #include "wl1251_reg.h"
index dd3cee6ea5bb6d486b27973d95f9fa5a35e842f1..4134f4495b95313c14552cae60cf00723291ec5b 100644 (file)
@@ -313,7 +313,7 @@ struct wl1271_if_operations {
                     bool fixed);
        void (*reset)(struct wl1271 *wl);
        void (*init)(struct wl1271 *wl);
-       void (*power)(struct wl1271 *wl, bool enable);
+       int (*power)(struct wl1271 *wl, bool enable);
        struct device* (*dev)(struct wl1271 *wl);
        void (*enable_irq)(struct wl1271 *wl);
        void (*disable_irq)(struct wl1271 *wl);
@@ -330,6 +330,7 @@ struct wl1271 {
 
        void (*set_power)(bool enable);
        int irq;
+       int ref_clock;
 
        spinlock_t wl_lock;
 
index f36430b0336d87d3c118962f0e573132ef7afa07..fc21db8108121740cc2711d7638461f2c93609af 100644 (file)
@@ -457,17 +457,20 @@ int wl1271_boot(struct wl1271 *wl)
 {
        int ret = 0;
        u32 tmp, clk, pause;
+       int ref_clock = wl->ref_clock;
 
        wl1271_boot_hw_version(wl);
 
-       if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4)
+       if (ref_clock == 0 || ref_clock == 2 || ref_clock == 4)
                /* ref clk: 19.2/38.4/38.4-XTAL */
                clk = 0x3;
-       else if (REF_CLOCK == 1 || REF_CLOCK == 3)
+       else if (ref_clock == 1 || ref_clock == 3)
                /* ref clk: 26/52 */
                clk = 0x5;
+       else
+               return -EINVAL;
 
-       if (REF_CLOCK != 0) {
+       if (ref_clock != 0) {
                u16 val;
                /* Set clock type (open drain) */
                val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
@@ -516,7 +519,7 @@ int wl1271_boot(struct wl1271 *wl)
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
        /* 2 */
-       clk |= (REF_CLOCK << 1) << 4;
+       clk |= (ref_clock << 1) << 4;
        wl1271_write32(wl, DRPW_SCRATCH_START, clk);
 
        wl1271_set_partition(wl, &part_table[PART_WORK]);
index f829699d597e1c00faff1eee7afe9c642e951b40..f73b0b15a280c3c1e0c20cc2019ff88fd8f1dbd9 100644 (file)
@@ -46,7 +46,6 @@ struct wl1271_static_data {
 /* delay between retries */
 #define INIT_LOOP_DELAY 50
 
-#define REF_CLOCK            2
 #define WU_COUNTER_PAUSE_VAL 0x3FF
 #define WELP_ARM_COMMAND_VAL 0x4
 
index bc806c74c63ab4214d70008bb58fa95a22546395..c1f92e65ded0b8c2895cce3b634562bb2b329b97 100644 (file)
@@ -144,10 +144,13 @@ static inline void wl1271_power_off(struct wl1271 *wl)
        clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 }
 
-static inline void wl1271_power_on(struct wl1271 *wl)
+static inline int wl1271_power_on(struct wl1271 *wl)
 {
-       wl->if_ops->power(wl, true);
-       set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+       int ret = wl->if_ops->power(wl, true);
+       if (ret == 0)
+               set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+
+       return ret;
 }
 
 
index 8e55cf8d509d8e4faa0802e3dc2d018aab97bbdf..776cd7c4114806936650ef3f8edde5b2037df66c 100644 (file)
@@ -621,7 +621,9 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
        int ret = 0;
 
        msleep(WL1271_PRE_POWER_ON_SLEEP);
-       wl1271_power_on(wl);
+       ret = wl1271_power_on(wl);
+       if (ret < 0)
+               goto out;
        msleep(WL1271_POWER_ON_SLEEP);
        wl1271_io_reset(wl);
        wl1271_io_init(wl);
@@ -1632,7 +1634,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       if ((changed && BSS_CHANGED_BEACON_INT) &&
+       if ((changed & BSS_CHANGED_BEACON_INT) &&
            (wl->bss_type == BSS_TYPE_IBSS)) {
                wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
                        bss_conf->beacon_int);
@@ -1641,7 +1643,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                do_join = true;
        }
 
-       if ((changed && BSS_CHANGED_BEACON) &&
+       if ((changed & BSS_CHANGED_BEACON) &&
            (wl->bss_type == BSS_TYPE_IBSS)) {
                struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
 
index 019aa79cd9dfc8cdc25c49973c93d5ecf6592d0f..94da5dd7723c14ef70f4bfe531c508941b48603b 100644 (file)
@@ -76,7 +76,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
 
 static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
 {
-       struct ieee80211_rx_status rx_status;
        struct wl1271_rx_descriptor *desc;
        struct sk_buff *skb;
        u16 *fc;
@@ -109,14 +108,13 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
        if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
                beacon = 1;
 
-       wl1271_rx_status(wl, desc, &rx_status, beacon);
+       wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
 
        wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len,
                     beacon ? "beacon" : "");
 
        skb_trim(skb, skb->len - desc->pad_len);
 
-       memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
        ieee80211_rx_ni(wl->hw, skb);
 }
 
index 7059b5cccf0f8fff9651017607e4d18b75d78b30..f2f04663627c2b43fb80b42c249748e4721ed30d 100644 (file)
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
 #include <linux/gpio.h>
+#include <linux/wl12xx.h>
 
 #include "wl1271.h"
 #include "wl12xx_80211.h"
 #include "wl1271_io.h"
 
-
-#define RX71_WL1271_IRQ_GPIO           42
-
 #ifndef SDIO_VENDOR_ID_TI
 #define SDIO_VENDOR_ID_TI              0x0097
 #endif
@@ -107,6 +105,8 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
        int ret;
        struct sdio_func *func = wl_to_func(wl);
 
+       sdio_claim_host(func);
+
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
                ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
                wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
@@ -122,9 +122,10 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
                wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
        }
 
+       sdio_release_host(func);
+
        if (ret)
                wl1271_error("sdio read failed (%d)", ret);
-
 }
 
 static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
@@ -133,6 +134,8 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
        int ret;
        struct sdio_func *func = wl_to_func(wl);
 
+       sdio_claim_host(func);
+
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
                sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
                wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
@@ -147,26 +150,45 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
                else
                        ret = sdio_memcpy_toio(func, addr, buf, len);
        }
+
+       sdio_release_host(func);
+
        if (ret)
                wl1271_error("sdio write failed (%d)", ret);
+}
+
+static int wl1271_sdio_power_on(struct wl1271 *wl)
+{
+       struct sdio_func *func = wl_to_func(wl);
+
+       sdio_claim_host(func);
+       sdio_enable_func(func);
+       sdio_release_host(func);
 
+       return 0;
 }
 
-static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+static int wl1271_sdio_power_off(struct wl1271 *wl)
 {
        struct sdio_func *func = wl_to_func(wl);
 
+       sdio_claim_host(func);
+       sdio_disable_func(func);
+       sdio_release_host(func);
+
+       return 0;
+}
+
+static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+{
        /* Let the SDIO stack handle wlan_enable control, so we
         * keep host claimed while wlan is in use to keep wl1271
         * alive.
         */
-       if (enable) {
-               sdio_claim_host(func);
-               sdio_enable_func(func);
-       } else {
-               sdio_disable_func(func);
-               sdio_release_host(func);
-       }
+       if (enable)
+               return wl1271_sdio_power_on(wl);
+       else
+               return wl1271_sdio_power_off(wl);
 }
 
 static struct wl1271_if_operations sdio_ops = {
@@ -184,6 +206,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
                                  const struct sdio_device_id *id)
 {
        struct ieee80211_hw *hw;
+       const struct wl12xx_platform_data *wlan_data;
        struct wl1271 *wl;
        int ret;
 
@@ -203,13 +226,16 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        /* Grab access to FN0 for ELP reg. */
        func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 
-       wl->irq = gpio_to_irq(RX71_WL1271_IRQ_GPIO);
-       if (wl->irq < 0) {
-               ret = wl->irq;
-               wl1271_error("could not get irq!");
+       wlan_data = wl12xx_get_platform_data();
+       if (IS_ERR(wlan_data)) {
+               ret = PTR_ERR(wlan_data);
+               wl1271_error("missing wlan platform data: %d", ret);
                goto out_free;
        }
 
+       wl->irq = wlan_data->irq;
+       wl->ref_clock = wlan_data->board_ref_clock;
+
        ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
index 4cb99c541e2abb528f63492a3c3736691242be04..ced0a9e2c7e14c02faa5d89b917222b2d89bf573 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/module.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/wl12xx.h>
+#include <linux/wl12xx.h>
 #include <linux/slab.h>
 
 #include "wl1271.h"
@@ -312,10 +312,12 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
        return IRQ_HANDLED;
 }
 
-static void wl1271_spi_set_power(struct wl1271 *wl, bool enable)
+static int wl1271_spi_set_power(struct wl1271 *wl, bool enable)
 {
        if (wl->set_power)
                wl->set_power(enable);
+
+       return 0;
 }
 
 static struct wl1271_if_operations spi_ops = {
@@ -370,6 +372,8 @@ static int __devinit wl1271_probe(struct spi_device *spi)
                goto out_free;
        }
 
+       wl->ref_clock = pdata->board_ref_clock;
+
        wl->irq = spi->irq;
        if (wl->irq < 0) {
                wl1271_error("irq missing in platform data");
diff --git a/drivers/net/wireless/wl12xx/wl12xx_platform_data.c b/drivers/net/wireless/wl12xx/wl12xx_platform_data.c
new file mode 100644 (file)
index 0000000..973b110
--- /dev/null
@@ -0,0 +1,28 @@
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/wl12xx.h>
+
+static const struct wl12xx_platform_data *platform_data;
+
+int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
+{
+       if (platform_data)
+               return -EBUSY;
+       if (!data)
+               return -EINVAL;
+
+       platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
+       if (!platform_data)
+               return -ENOMEM;
+
+       return 0;
+}
+
+const struct wl12xx_platform_data *wl12xx_get_platform_data(void)
+{
+       if (!platform_data)
+               return ERR_PTR(-ENODEV);
+
+       return platform_data;
+}
+EXPORT_SYMBOL(wl12xx_get_platform_data);
index 31603e8b558132818f37936ae0a99113bf43a54f..f0518b0278a927b0998659daf1aed1c5e7793bc6 100644 (file)
@@ -1020,6 +1020,8 @@ enum nl80211_attrs {
  * @NL80211_IFTYPE_WDS: wireless distribution interface
  * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
  * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -1036,6 +1038,8 @@ enum nl80211_iftype {
        NL80211_IFTYPE_WDS,
        NL80211_IFTYPE_MONITOR,
        NL80211_IFTYPE_MESH_POINT,
+       NL80211_IFTYPE_P2P_CLIENT,
+       NL80211_IFTYPE_P2P_GO,
 
        /* keep last */
        NUM_NL80211_IFTYPES,
similarity index 82%
rename from include/linux/spi/wl12xx.h
rename to include/linux/wl12xx.h
index a20bccf0b5c2679c7e28b0ac859380823b324931..95deae3968f4b5f24bb40a6b53b1dc9fbbc928cf 100644 (file)
  *
  */
 
-#ifndef _LINUX_SPI_WL12XX_H
-#define _LINUX_SPI_WL12XX_H
+#ifndef _LINUX_WL12XX_H
+#define _LINUX_WL12XX_H
 
 struct wl12xx_platform_data {
        void (*set_power)(bool enable);
        /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
        int irq;
        bool use_eeprom;
+       int board_ref_clock;
 };
 
+int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
+const struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+
 #endif
index 4c8c727d0cca04abe49fd01594378d38ddf12f3e..a0613ff62c97b3e6811f15f28b5950e8b3644e0d 100644 (file)
@@ -2558,49 +2558,36 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 /* wiphy_printk helpers, similar to dev_printk */
 
 #define wiphy_printk(level, wiphy, format, args...)            \
-       printk(level "%s: " format, wiphy_name(wiphy), ##args)
+       dev_printk(level, &(wiphy)->dev, format, ##args)
 #define wiphy_emerg(wiphy, format, args...)                    \
-       wiphy_printk(KERN_EMERG, wiphy, format, ##args)
+       dev_emerg(&(wiphy)->dev, format, ##args)
 #define wiphy_alert(wiphy, format, args...)                    \
-       wiphy_printk(KERN_ALERT, wiphy, format, ##args)
+       dev_alert(&(wiphy)->dev, format, ##args)
 #define wiphy_crit(wiphy, format, args...)                     \
-       wiphy_printk(KERN_CRIT, wiphy, format, ##args)
+       dev_crit(&(wiphy)->dev, format, ##args)
 #define wiphy_err(wiphy, format, args...)                      \
-       wiphy_printk(KERN_ERR, wiphy, format, ##args)
+       dev_err(&(wiphy)->dev, format, ##args)
 #define wiphy_warn(wiphy, format, args...)                     \
-       wiphy_printk(KERN_WARNING, wiphy, format, ##args)
+       dev_warn(&(wiphy)->dev, format, ##args)
 #define wiphy_notice(wiphy, format, args...)                   \
-       wiphy_printk(KERN_NOTICE, wiphy, format, ##args)
+       dev_notice(&(wiphy)->dev, format, ##args)
 #define wiphy_info(wiphy, format, args...)                     \
-       wiphy_printk(KERN_INFO, wiphy, format, ##args)
+       dev_info(&(wiphy)->dev, format, ##args)
 
-int wiphy_debug(const struct wiphy *wiphy, const char *format, ...)
-       __attribute__ ((format (printf, 2, 3)));
-
-#if defined(DEBUG)
-#define wiphy_dbg(wiphy, format, args...)                      \
+#define wiphy_debug(wiphy, format, args...)                    \
        wiphy_printk(KERN_DEBUG, wiphy, format, ##args)
-#elif defined(CONFIG_DYNAMIC_DEBUG)
+
 #define wiphy_dbg(wiphy, format, args...)                      \
-       dynamic_pr_debug("%s: " format, wiphy_name(wiphy), ##args)
-#else
-#define wiphy_dbg(wiphy, format, args...)                              \
-({                                                                     \
-       if (0)                                                          \
-               wiphy_printk(KERN_DEBUG, wiphy, format, ##args);        \
-       0;                                                              \
-})
-#endif
+       dev_dbg(&(wiphy)->dev, format, ##args)
 
 #if defined(VERBOSE_DEBUG)
 #define wiphy_vdbg     wiphy_dbg
 #else
-
 #define wiphy_vdbg(wiphy, format, args...)                             \
 ({                                                                     \
        if (0)                                                          \
                wiphy_printk(KERN_DEBUG, wiphy, format, ##args);        \
-               0;                                                      \
+       0;                                                              \
 })
 #endif
 
index f91fc331369bb22f0ec6463e0511e180cf1fa902..12a49f0ba32c2845b9ded96f30a9abde07805d9a 100644 (file)
@@ -769,6 +769,8 @@ struct ieee80211_channel_switch {
  * @bss_conf: BSS configuration for this interface, either our own
  *     or the BSS we're associated to
  * @addr: address of this interface
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ *     interface, i.e. a GO or p2p-sta respectively
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
  */
@@ -776,6 +778,7 @@ struct ieee80211_vif {
        enum nl80211_iftype type;
        struct ieee80211_bss_conf bss_conf;
        u8 addr[ETH_ALEN];
+       bool p2p;
        /* must be last */
        u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
 };
@@ -1701,7 +1704,7 @@ struct ieee80211_ops {
                             struct ieee80211_vif *vif);
        int (*change_interface)(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
-                               enum nl80211_iftype new_type);
+                               enum nl80211_iftype new_type, bool p2p);
        void (*remove_interface)(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif);
        int (*config)(struct ieee80211_hw *hw, u32 changed);
@@ -2293,6 +2296,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted);
  * This function allows the iterator function to sleep, when the iterator
  * function is atomic @ieee80211_iterate_active_interfaces_atomic can
  * be used.
+ * Does not iterate over a new interface during add_interface()
  *
  * @hw: the hardware struct of which the interfaces should be iterated over
  * @iterator: the iterator function to call
@@ -2310,6 +2314,7 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw,
  * hardware that are currently active and calls the callback for them.
  * This function requires the iterator callback function to be atomic,
  * if that is not desired, use @ieee80211_iterate_active_interfaces instead.
+ * Does not iterate over a new interface during add_interface()
  *
  * @hw: the hardware struct of which the interfaces should be iterated over
  * @iterator: the iterator function to call, cannot sleep
@@ -2719,4 +2724,26 @@ conf_is_ht(struct ieee80211_conf *conf)
        return conf->channel_type != NL80211_CHAN_NO_HT;
 }
 
+static inline enum nl80211_iftype
+ieee80211_iftype_p2p(enum nl80211_iftype type, bool p2p)
+{
+       if (p2p) {
+               switch (type) {
+               case NL80211_IFTYPE_STATION:
+                       return NL80211_IFTYPE_P2P_CLIENT;
+               case NL80211_IFTYPE_AP:
+                       return NL80211_IFTYPE_P2P_GO;
+               default:
+                       break;
+               }
+       }
+       return type;
+}
+
+static inline enum nl80211_iftype
+ieee80211_vif_type_p2p(struct ieee80211_vif *vif)
+{
+       return ieee80211_iftype_p2p(vif->type, vif->p2p);
+}
+
 #endif /* MAC80211_H */
index 5de1ca3f17b93bd13db9c4ba762fd2df8d7aa276..c981604b71e6d78fa1d13d7ec4d8d03976396819 100644 (file)
@@ -577,6 +577,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                                 struct sta_info *sta,
                                 struct station_parameters *params)
 {
+       unsigned long flags;
        u32 rates;
        int i, j;
        struct ieee80211_supported_band *sband;
@@ -585,7 +586,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 
        sband = local->hw.wiphy->bands[local->oper_channel->band];
 
-       spin_lock_bh(&sta->lock);
+       spin_lock_irqsave(&sta->flaglock, flags);
        mask = params->sta_flags_mask;
        set = params->sta_flags_set;
 
@@ -612,7 +613,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                if (set & BIT(NL80211_STA_FLAG_MFP))
                        sta->flags |= WLAN_STA_MFP;
        }
-       spin_unlock_bh(&sta->lock);
+       spin_unlock_irqrestore(&sta->flaglock, flags);
 
        /*
         * cfg80211 validates this (1-2007) and allows setting the AID
@@ -1150,15 +1151,26 @@ static int ieee80211_scan(struct wiphy *wiphy,
                          struct net_device *dev,
                          struct cfg80211_scan_request *req)
 {
-       struct ieee80211_sub_if_data *sdata;
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-           sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
-           (sdata->vif.type != NL80211_IFTYPE_AP || sdata->u.ap.beacon))
+       switch (ieee80211_vif_type_p2p(&sdata->vif)) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               if (sdata->local->ops->hw_scan)
+                       break;
+               /* FIXME: implement NoA while scanning in software */
+               return -EOPNOTSUPP;
+       case NL80211_IFTYPE_AP:
+               if (sdata->u.ap.beacon)
+                       return -EOPNOTSUPP;
+               break;
+       default:
                return -EOPNOTSUPP;
+       }
 
        return ieee80211_request_scan(sdata, req);
 }
index 32be11e4c4d92dbe9d34d8cae84d7e0a482e8882..5b24740fc0b0698c65cc1141c981631160a1efc8 100644 (file)
@@ -11,7 +11,7 @@ __ieee80211_get_channel_mode(struct ieee80211_local *local,
 {
        struct ieee80211_sub_if_data *sdata;
 
-       WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+       lockdep_assert_held(&local->iflist_mtx);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata == ignore)
index 6064b7b09e0142f719b1e744ee124267f8d61d1a..16983825f8e856c3bfcf83c9afe5aa25673b0a70 100644 (file)
@@ -56,14 +56,14 @@ static inline int drv_add_interface(struct ieee80211_local *local,
 
 static inline int drv_change_interface(struct ieee80211_local *local,
                                       struct ieee80211_sub_if_data *sdata,
-                                      enum nl80211_iftype type)
+                                      enum nl80211_iftype type, bool p2p)
 {
        int ret;
 
        might_sleep();
 
-       trace_drv_change_interface(local, sdata, type);
-       ret = local->ops->change_interface(&local->hw, &sdata->vif, type);
+       trace_drv_change_interface(local, sdata, type, p2p);
+       ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
        trace_drv_return_int(local, ret);
        return ret;
 }
index f6f3d89e43faa206a56888b200030e8f3ed151e8..6831fb1641c8a375d64059a4f83ae6eee2d631eb 100644 (file)
@@ -25,12 +25,14 @@ static inline void trace_ ## name(proto) {}
 #define STA_PR_FMT     " sta:%pM"
 #define STA_PR_ARG     __entry->sta_addr
 
-#define VIF_ENTRY      __field(enum nl80211_iftype, vif_type) __field(void *, sdata) \
+#define VIF_ENTRY      __field(enum nl80211_iftype, vif_type) __field(void *, sdata)   \
+                       __field(bool, p2p)                                              \
                        __string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
-#define VIF_ASSIGN     __entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \
+#define VIF_ASSIGN     __entry->vif_type = sdata->vif.type; __entry->sdata = sdata;    \
+                       __entry->p2p = sdata->vif.p2p;                                  \
                        __assign_str(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
-#define VIF_PR_FMT     " vif:%s(%d)"
-#define VIF_PR_ARG     __get_str(vif_name), __entry->vif_type
+#define VIF_PR_FMT     " vif:%s(%d%s)"
+#define VIF_PR_ARG     __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
 
 /*
  * Tracing for driver callbacks.
@@ -139,25 +141,28 @@ TRACE_EVENT(drv_add_interface,
 TRACE_EVENT(drv_change_interface,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
-                enum nl80211_iftype type),
+                enum nl80211_iftype type, bool p2p),
 
-       TP_ARGS(local, sdata, type),
+       TP_ARGS(local, sdata, type, p2p),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
                VIF_ENTRY
                __field(u32, new_type)
+               __field(bool, new_p2p)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
                __entry->new_type = type;
+               __entry->new_p2p = p2p;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT  VIF_PR_FMT " new type:%d",
-               LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type
+               LOCAL_PR_FMT  VIF_PR_FMT " new type:%d%s",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type,
+               __entry->new_p2p ? "/p2p" : ""
        )
 );
 
index 4e635e2fabdb14d640b3e5832dc00f45ecb65c4a..9346a6b0f40008323cfa403bd3e7a6fce362a89b 100644 (file)
@@ -1041,6 +1041,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                  struct sk_buff *skb);
+void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
 
 /* IBSS code */
 void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
index c1cc200ac81f29a7d50d494e6de45277b99f6683..66785739dad378fe5bdd10904d8c59586951d131 100644 (file)
@@ -188,6 +188,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                break;
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
                /* cannot happen */
                WARN_ON(1);
                break;
@@ -280,6 +282,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
                        netif_carrier_on(dev);
        }
 
+       set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
        if (sdata->vif.type == NL80211_IFTYPE_WDS) {
                /* Create STA entry for the WDS peer */
                sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
@@ -331,8 +335,6 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
 
        netif_tx_start_all_queues(dev);
 
-       set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
        return 0;
  err_del_interface:
        drv_remove_interface(local, &sdata->vif);
@@ -343,6 +345,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
        sdata->bss = NULL;
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                list_del(&sdata->u.vlan.list);
+       clear_bit(SDATA_STATE_RUNNING, &sdata->state);
        return res;
 }
 
@@ -843,6 +846,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
 
        /* and set some type-dependent values */
        sdata->vif.type = type;
+       sdata->vif.p2p = false;
        sdata->dev->netdev_ops = &ieee80211_dataif_ops;
        sdata->wdev.iftype = type;
 
@@ -856,10 +860,20 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        INIT_WORK(&sdata->work, ieee80211_iface_work);
 
        switch (type) {
+       case NL80211_IFTYPE_P2P_GO:
+               type = NL80211_IFTYPE_AP;
+               sdata->vif.type = type;
+               sdata->vif.p2p = true;
+               /* fall through */
        case NL80211_IFTYPE_AP:
                skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
                INIT_LIST_HEAD(&sdata->u.ap.vlans);
                break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+               type = NL80211_IFTYPE_STATION;
+               sdata->vif.type = type;
+               sdata->vif.p2p = true;
+               /* fall through */
        case NL80211_IFTYPE_STATION:
                ieee80211_sta_setup_sdata(sdata);
                break;
@@ -893,6 +907,8 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        int ret, err;
+       enum nl80211_iftype internal_type = type;
+       bool p2p = false;
 
        ASSERT_RTNL();
 
@@ -925,11 +941,19 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
                 * code isn't prepared to handle).
                 */
                break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+               p2p = true;
+               internal_type = NL80211_IFTYPE_STATION;
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               p2p = true;
+               internal_type = NL80211_IFTYPE_AP;
+               break;
        default:
                return -EBUSY;
        }
 
-       ret = ieee80211_check_concurrent_iface(sdata, type);
+       ret = ieee80211_check_concurrent_iface(sdata, internal_type);
        if (ret)
                return ret;
 
@@ -937,7 +961,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_teardown_sdata(sdata->dev);
 
-       ret = drv_change_interface(local, sdata, type);
+       ret = drv_change_interface(local, sdata, internal_type, p2p);
        if (ret)
                type = sdata->vif.type;
 
@@ -956,7 +980,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
 
        ASSERT_RTNL();
 
-       if (type == sdata->vif.type)
+       if (type == ieee80211_vif_type_p2p(&sdata->vif))
                return 0;
 
        /* Setting ad-hoc mode on non-IBSS channel is not supported. */
index 3570f8c2bb401d3d4d20cbac6dbc0fddfb395235..6a63d1abd14dab5117778bf524c630d19cfaf01e 100644 (file)
@@ -49,7 +49,7 @@ static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 
 static void assert_key_lock(struct ieee80211_local *local)
 {
-       WARN_ON(!mutex_is_locked(&local->key_mtx));
+       lockdep_assert_held(&local->key_mtx);
 }
 
 static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
index b8cf2821f00d9a9956925812d1eba77211623285..fda97bb0018b3de97ace2d27ecf8c5d9e3124d11 100644 (file)
@@ -305,6 +305,9 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
 
        trace_api_restart_hw(local);
 
+       /* wait for scan work complete */
+       flush_workqueue(local->workqueue);
+
        WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
                "%s called with hardware scan in progress\n", __func__);
 
@@ -456,6 +459,21 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
                        BIT(IEEE80211_STYPE_DEAUTH >> 4) |
                        BIT(IEEE80211_STYPE_ACTION >> 4),
        },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                       BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                       BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                       BIT(IEEE80211_STYPE_AUTH >> 4) |
+                       BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                       BIT(IEEE80211_STYPE_ACTION >> 4),
+       },
 };
 
 struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
index 0cb822cc12e9c52618ddb18d5503db43765b9f11..8b733cf6f3ea111b0559e9e21744123a74ed4682 100644 (file)
@@ -92,7 +92,7 @@ enum rx_mgmt_action {
 /* utils */
 static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
 {
-       WARN_ON(!mutex_is_locked(&ifmgd->mtx));
+       lockdep_assert_held(&ifmgd->mtx);
 }
 
 /*
@@ -115,7 +115,7 @@ static void run_again(struct ieee80211_if_managed *ifmgd,
                mod_timer(&ifmgd->timer, timeout);
 }
 
-static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
+void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
 {
        if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
                return;
@@ -124,6 +124,19 @@ static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
                  round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
 }
 
+void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               return;
+
+       mod_timer(&sdata->u.mgd.conn_mon_timer,
+                 round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+
+       ifmgd->probe_send_count = 0;
+}
+
 static int ecw2cw(int ecw)
 {
        return (1 << ecw) - 1;
@@ -1018,21 +1031,26 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
        if (is_multicast_ether_addr(hdr->addr1))
                return;
 
-       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
-               return;
-
-       mod_timer(&sdata->u.mgd.conn_mon_timer,
-                 round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+       ieee80211_sta_reset_conn_monitor(sdata);
 }
 
 static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *ssid;
+       u8 *dst = ifmgd->associated->bssid;
+       u8 unicast_limit = max(1, IEEE80211_MAX_PROBE_TRIES - 3);
+
+       /*
+        * Try sending broadcast probe requests for the last three
+        * probe requests after the first ones failed since some
+        * buggy APs only support broadcast probe requests.
+        */
+       if (ifmgd->probe_send_count >= unicast_limit)
+               dst = NULL;
 
        ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
-       ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
-                                ssid + 2, ssid[1], NULL, 0);
+       ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
 
        ifmgd->probe_send_count++;
        ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
@@ -1381,7 +1399,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
         * Also start the timer that will detect beacon loss.
         */
        ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
-       mod_beacon_timer(sdata);
+       ieee80211_sta_reset_beacon_monitor(sdata);
 
        return true;
 }
@@ -1484,7 +1502,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                 * we have or will be receiving any beacons or data, so let's
                 * schedule the timers again, just in case.
                 */
-               mod_beacon_timer(sdata);
+               ieee80211_sta_reset_beacon_monitor(sdata);
 
                mod_timer(&ifmgd->conn_mon_timer,
                          round_jiffies_up(jiffies +
@@ -1610,7 +1628,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * Push the beacon loss detection into the future since
         * we are processing a beacon from the AP just now.
         */
-       mod_beacon_timer(sdata);
+       ieee80211_sta_reset_beacon_monitor(sdata);
 
        ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
        ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
index eeacaa59380a4c4cc91fe7f48d8ca622a13396b0..4b564091e51d52ae983058a8e4dfa9b10f704152 100644 (file)
 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        local->offchannel_ps_enabled = false;
 
        /* FIXME: what to do when local->pspolling is true? */
 
        del_timer_sync(&local->dynamic_ps_timer);
+       del_timer_sync(&ifmgd->bcn_mon_timer);
+       del_timer_sync(&ifmgd->conn_mon_timer);
+
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -85,6 +89,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
                mod_timer(&local->dynamic_ps_timer, jiffies +
                          msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
        }
+
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
 }
 
 void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
index ac205a33690f90f8058cdd98e0ea5982657bcb18..c0368152b721699f298023b72295dca894f710d1 100644 (file)
@@ -2588,9 +2588,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
                if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
                        return 0;
                break;
-       case NL80211_IFTYPE_MONITOR:
-       case NL80211_IFTYPE_UNSPECIFIED:
-       case NUM_NL80211_IFTYPES:
+       default:
                /* should never get here */
                WARN_ON(1);
                break;
index 687077e49dc6ecaa44648db98a25138a12c14fd2..44e10a9de0a71d0dc9bf183708ecee3226d80fe3 100644 (file)
@@ -125,7 +125,7 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
                                    lockdep_is_held(&local->sta_mtx));
        while (sta) {
                if ((sta->sdata == sdata ||
-                    sta->sdata->bss == sdata->bss) &&
+                    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
                    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
                        break;
                sta = rcu_dereference_check(sta->hnext,
@@ -280,7 +280,7 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async)
        unsigned long flags;
        int err = 0;
 
-       WARN_ON(!mutex_is_locked(&local->sta_mtx));
+       lockdep_assert_held(&local->sta_mtx);
 
        /* notify driver */
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
index ccf373788ce9929dc2599034f4db310d1f9ed324..e1733dcb58a73025ba609b2a9919524ec2dd51c0 100644 (file)
@@ -1609,6 +1609,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       hdr = (struct ieee80211_hdr *) skb->data;
        info->control.vif = &sdata->vif;
 
        if (ieee80211_vif_is_mesh(&sdata->vif) &&
index bd40b11d5ab9e514d7e75f887baebf65352e0370..737f4267c3357d291fd54a6ce61d514636cd00ca 100644 (file)
@@ -474,16 +474,10 @@ void ieee80211_iterate_active_interfaces(
 
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
-               case NUM_NL80211_IFTYPES:
-               case NL80211_IFTYPE_UNSPECIFIED:
                case NL80211_IFTYPE_MONITOR:
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
-               case NL80211_IFTYPE_AP:
-               case NL80211_IFTYPE_STATION:
-               case NL80211_IFTYPE_ADHOC:
-               case NL80211_IFTYPE_WDS:
-               case NL80211_IFTYPE_MESH_POINT:
+               default:
                        break;
                }
                if (ieee80211_sdata_running(sdata))
@@ -508,16 +502,10 @@ void ieee80211_iterate_active_interfaces_atomic(
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
-               case NUM_NL80211_IFTYPES:
-               case NL80211_IFTYPE_UNSPECIFIED:
                case NL80211_IFTYPE_MONITOR:
                case NL80211_IFTYPE_AP_VLAN:
                        continue;
-               case NL80211_IFTYPE_AP:
-               case NL80211_IFTYPE_STATION:
-               case NL80211_IFTYPE_ADHOC:
-               case NL80211_IFTYPE_WDS:
-               case NL80211_IFTYPE_MESH_POINT:
+               default:
                        break;
                }
                if (ieee80211_sdata_running(sdata))
@@ -1193,6 +1181,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        break;
                case NL80211_IFTYPE_UNSPECIFIED:
                case NUM_NL80211_IFTYPES:
+               case NL80211_IFTYPE_P2P_CLIENT:
+               case NL80211_IFTYPE_P2P_GO:
                        WARN_ON(1);
                        break;
                }
@@ -1296,9 +1286,9 @@ void ieee80211_recalc_smps(struct ieee80211_local *local,
        int count = 0;
 
        if (forsdata)
-               WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+               lockdep_assert_held(&forsdata->u.mgd.mtx);
 
-       WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+       lockdep_assert_held(&local->iflist_mtx);
 
        /*
         * This function could be improved to handle multiple
index d587ad284b3d847fb4e4eefe293f305500015f22..9c21ebf9780ea21ff22c9326da29a423061d088b 100644 (file)
@@ -729,6 +729,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                        dev->ethtool_ops = &cfg80211_ethtool_ops;
 
                if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+                    wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
                     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
                        dev->priv_flags |= IFF_DONT_BRIDGE;
                break;
@@ -737,6 +738,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                case NL80211_IFTYPE_ADHOC:
                        cfg80211_leave_ibss(rdev, dev, true);
                        break;
+               case NL80211_IFTYPE_P2P_CLIENT:
                case NL80211_IFTYPE_STATION:
                        wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
@@ -915,52 +917,3 @@ static void __exit cfg80211_exit(void)
        destroy_workqueue(cfg80211_wq);
 }
 module_exit(cfg80211_exit);
-
-static int ___wiphy_printk(const char *level, const struct wiphy *wiphy,
-                          struct va_format *vaf)
-{
-       if (!wiphy)
-               return printk("%s(NULL wiphy *): %pV", level, vaf);
-
-       return printk("%s%s: %pV", level, wiphy_name(wiphy), vaf);
-}
-
-int __wiphy_printk(const char *level, const struct wiphy *wiphy,
-                  const char *fmt, ...)
-{
-       struct va_format vaf;
-       va_list args;
-       int r;
-
-       va_start(args, fmt);
-
-       vaf.fmt = fmt;
-       vaf.va = &args;
-
-       r = ___wiphy_printk(level, wiphy, &vaf);
-       va_end(args);
-
-       return r;
-}
-EXPORT_SYMBOL(__wiphy_printk);
-
-#define define_wiphy_printk_level(func, kern_level)            \
-int func(const struct wiphy *wiphy, const char *fmt, ...)      \
-{                                                              \
-       struct va_format vaf;                                   \
-       va_list args;                                           \
-       int r;                                                  \
-                                                               \
-       va_start(args, fmt);                                    \
-                                                               \
-       vaf.fmt = fmt;                                          \
-       vaf.va = &args;                                         \
-                                                               \
-       r = ___wiphy_printk(kern_level, wiphy, &vaf);           \
-       va_end(args);                                           \
-                                                               \
-       return r;                                               \
-}                                                              \
-EXPORT_SYMBOL(func);
-
-define_wiphy_printk_level(wiphy_debug, KERN_DEBUG);
index 58ab2c791d287c58426458dea9d685ce49770a2f..37580e090a3db6bd1daf05efe14d42be7a63b5ff 100644 (file)
@@ -95,7 +95,10 @@ extern struct mutex cfg80211_mutex;
 extern struct list_head cfg80211_rdev_list;
 extern int cfg80211_rdev_list_generation;
 
-#define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex))
+static inline void assert_cfg80211_lock(void)
+{
+       lockdep_assert_held(&cfg80211_mutex);
+}
 
 /*
  * You can use this to mark a wiphy_idx as not having an associated wiphy.
@@ -202,8 +205,8 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
        mutex_unlock(&wdev->mtx);
 }
 
-#define ASSERT_RDEV_LOCK(rdev) WARN_ON(!mutex_is_locked(&(rdev)->mtx));
-#define ASSERT_WDEV_LOCK(wdev) WARN_ON(!mutex_is_locked(&(wdev)->mtx));
+#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx)
+#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
 
 enum cfg80211_event_type {
        EVENT_CONNECT_RESULT,
index 8515b1e5c5781075897a769b46608f2fe89f4cd7..46f3711608960b7ba42d6675219530b2c9886e2d 100644 (file)
@@ -882,7 +882,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
                if (!wdev->current_bss ||
                    memcmp(wdev->current_bss->pub.bssid, mgmt->bssid,
                           ETH_ALEN) != 0 ||
-                   (wdev->iftype == NL80211_IFTYPE_STATION &&
+                   ((wdev->iftype == NL80211_IFTYPE_STATION ||
+                     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
                     memcmp(wdev->current_bss->pub.bssid, mgmt->da,
                            ETH_ALEN) != 0)) {
                        wdev_unlock(wdev);
index 85a23de7bff346204c456519ca6a8692bfac7eb9..f15b1af2c768ca8007902b045e998456d38d595a 100644 (file)
@@ -410,12 +410,14 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        switch (wdev->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                if (wdev->sme_state != CFG80211_SME_CONNECTED)
                        return -ENOLINK;
                break;
@@ -766,7 +768,8 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
                wdev->iftype == NL80211_IFTYPE_AP ||
                wdev->iftype == NL80211_IFTYPE_WDS ||
                wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
-               wdev->iftype == NL80211_IFTYPE_MONITOR;
+               wdev->iftype == NL80211_IFTYPE_MONITOR ||
+               wdev->iftype == NL80211_IFTYPE_P2P_GO;
 }
 
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
@@ -833,7 +836,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev;
        struct net_device *netdev = NULL;
        struct wireless_dev *wdev;
-       int result, rem_txq_params = 0;
+       int result = 0, rem_txq_params = 0;
        struct nlattr *nl_txq_params;
        u32 changed;
        u8 retry_short = 0, retry_long = 0;
@@ -1693,7 +1696,8 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto unlock_rtnl;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -1785,7 +1789,8 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2128,10 +2133,12 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
        switch (dev->ieee80211_ptr->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
                /* disallow mesh-specific things */
                if (params.plink_action)
                        err = -EINVAL;
                break;
+       case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                /* disallow everything but AUTHORIZED flag */
                if (params.plink_action)
@@ -2233,7 +2240,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                goto out_rtnl;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
                err = -EINVAL;
                goto out;
        }
@@ -2286,7 +2294,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
                err = -EINVAL;
                goto out;
        }
@@ -2660,7 +2669,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3363,6 +3373,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        }
 
        switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                if (intbss == wdev->current_bss)
                        NLA_PUT_U32(msg, NL80211_BSS_STATUS,
@@ -3649,7 +3660,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3804,7 +3816,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3888,7 +3901,8 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3954,7 +3968,8 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4332,7 +4347,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto unlock_rtnl;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4408,7 +4424,8 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto unlock_rtnl;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4496,7 +4513,8 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
        pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
        pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4541,7 +4559,8 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out_rtnl;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4823,7 +4842,8 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
                goto unlock_rtnl;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -4875,7 +4895,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -5093,7 +5114,8 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
                goto unlock_rdev;
        }
 
-       if (wdev->iftype != NL80211_IFTYPE_STATION) {
+       if (wdev->iftype != NL80211_IFTYPE_STATION &&
+           wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                err = -EOPNOTSUPP;
                goto unlock_rdev;
        }
index 1332c445d1c78a6f41ba2e6aba13eaf8cade675f..c774bc0f155ecb381ae3069f486f661ee31aac1c 100644 (file)
@@ -14,6 +14,7 @@
  * See COPYING for more details.
  */
 
+#include <linux/kernel.h>
 #include <net/cfg80211.h>
 #include <net/ieee80211_radiotap.h>
 #include <asm/unaligned.h>
@@ -45,7 +46,7 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = {
 };
 
 static const struct ieee80211_radiotap_namespace radiotap_ns = {
-       .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+       .n_bits = ARRAY_SIZE(rtap_namespace_sizes),
        .align_size = rtap_namespace_sizes,
 };
 
index b0d9a08447c9f7656b03ff6dfcb2971537a74b9d..d14bbf960c18223a5305b4b1cd33cd68ce49be76 100644 (file)
@@ -74,7 +74,11 @@ const struct ieee80211_regdomain *cfg80211_regdomain;
  *     - last_request
  */
 static DEFINE_MUTEX(reg_mutex);
-#define assert_reg_lock() WARN_ON(!mutex_is_locked(&reg_mutex))
+
+static inline void assert_reg_lock(void)
+{
+       lockdep_assert_held(&reg_mutex);
+}
 
 /* Used to queue up regulatory hints */
 static LIST_HEAD(reg_requests_list);
index a8c2d6b877aeda894cb80328d015bc3494e000c9..f161b9844542a2adcfa82dfc83fcd23068a4ee46 100644 (file)
@@ -411,7 +411,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
        if (wdev->sme_state != CFG80211_SME_CONNECTING)
@@ -548,7 +549,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
        if (wdev->sme_state != CFG80211_SME_CONNECTED)
@@ -644,7 +646,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
        if (wdev->sme_state != CFG80211_SME_CONNECTED)
index bca32eb8f446a90161c314e55042627953e84328..fb5448f7d55af55b8ee8220ef813880993d0b069 100644 (file)
@@ -326,7 +326,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
        case cpu_to_le16(IEEE80211_FCTL_TODS):
                if (unlikely(iftype != NL80211_IFTYPE_AP &&
-                            iftype != NL80211_IFTYPE_AP_VLAN))
+                            iftype != NL80211_IFTYPE_AP_VLAN &&
+                            iftype != NL80211_IFTYPE_P2P_GO))
                        return -1;
                break;
        case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
@@ -354,7 +355,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                break;
        case cpu_to_le16(IEEE80211_FCTL_FROMDS):
                if ((iftype != NL80211_IFTYPE_STATION &&
-                   iftype != NL80211_IFTYPE_MESH_POINT) ||
+                    iftype != NL80211_IFTYPE_P2P_CLIENT &&
+                    iftype != NL80211_IFTYPE_MESH_POINT) ||
                    (is_multicast_ether_addr(dst) &&
                     !compare_ether_addr(src, addr)))
                        return -1;
@@ -431,6 +433,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
                /* DA BSSID SA */
                memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -439,6 +442,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                hdrlen = 24;
                break;
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
                /* BSSID SA DA */
                memcpy(hdr.addr1, bssid, ETH_ALEN);
@@ -778,7 +782,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 
        /* if it's part of a bridge, reject changing type to station/ibss */
        if ((dev->priv_flags & IFF_BRIDGE_PORT) &&
-           (ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION))
+           (ntype == NL80211_IFTYPE_ADHOC ||
+            ntype == NL80211_IFTYPE_STATION ||
+            ntype == NL80211_IFTYPE_P2P_CLIENT))
                return -EBUSY;
 
        if (ntype != otype) {
@@ -789,6 +795,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                        cfg80211_leave_ibss(rdev, dev, false);
                        break;
                case NL80211_IFTYPE_STATION:
+               case NL80211_IFTYPE_P2P_CLIENT:
                        cfg80211_disconnect(rdev, dev,
                                            WLAN_REASON_DEAUTH_LEAVING, true);
                        break;
@@ -817,9 +824,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                        if (dev->ieee80211_ptr->use_4addr)
                                break;
                        /* fall through */
+               case NL80211_IFTYPE_P2P_CLIENT:
                case NL80211_IFTYPE_ADHOC:
                        dev->priv_flags |= IFF_DONT_BRIDGE;
                        break;
+               case NL80211_IFTYPE_P2P_GO:
                case NL80211_IFTYPE_AP:
                case NL80211_IFTYPE_AP_VLAN:
                case NL80211_IFTYPE_WDS: