Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:06:28 +0000 (15:06 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:06:28 +0000 (15:06 -0400)
173 files changed:
MAINTAINERS
drivers/bcma/Kconfig
drivers/bcma/main.c
drivers/bcma/scan.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/bmi.h
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htt.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/antenna.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_eeprom.h
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/hw-ops.h
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/main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/phy.h
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/trace.h
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/cw1200/wsm.h
drivers/net/wireless/hostap/hostap_main.c
drivers/net/wireless/iwlegacy/3945-rs.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965-rs.c
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/constants.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/power_legacy.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
drivers/net/wireless/rtlwifi/rtl8192cu/sw.h
drivers/net/wireless/ti/wlcore/main.c
drivers/ssb/Kconfig
drivers/ssb/driver_chipcommon_sflash.c
include/linux/bcma/bcma.h
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_pid_algo.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/scan.c
net/wireless/trace.h
net/wireless/util.c

index 6447bec..1ea2d04 100644 (file)
@@ -4362,7 +4362,7 @@ F:        drivers/net/wireless/iwlegacy/
 
 INTEL WIRELESS WIFI LINK (iwlwifi)
 M:     Johannes Berg <johannes.berg@intel.com>
-M:     Wey-Yi Guy <wey-yi.w.guy@intel.com>
+M:     Emmanuel Grumbach <emmanuel.grumbach@intel.com>
 M:     Intel Linux Wireless <ilw@linux.intel.com>
 L:     linux-wireless@vger.kernel.org
 W:     http://intellinuxwireless.org
index 380a200..7c081b3 100644 (file)
@@ -35,8 +35,14 @@ config BCMA_DRIVER_PCI_HOSTMODE
          PCI core hostmode operation (external PCI bus).
 
 config BCMA_HOST_SOC
-       bool
-       depends on BCMA_DRIVER_MIPS
+       bool "Support for BCMA in a SoC"
+       depends on BCMA
+       help
+         Host interface for a Broadcom AIX bus directly mapped into
+         the memory. This only works with the Broadcom SoCs from the
+         BCM47XX line.
+
+         If unsure, say N
 
 config BCMA_DRIVER_MIPS
        bool "BCMA Broadcom MIPS core driver"
index 0067422..90ee350 100644 (file)
@@ -237,7 +237,7 @@ int bcma_bus_register(struct bcma_bus *bus)
        err = bcma_bus_scan(bus);
        if (err) {
                bcma_err(bus, "Failed to scan: %d\n", err);
-               return -1;
+               return err;
        }
 
        /* Early init CC core */
index 8bffa5c..cd6b20f 100644 (file)
@@ -32,6 +32,18 @@ static const struct bcma_device_id_name bcma_bcm_device_names[] = {
        { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" },
        { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" },
        { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" },
+       { BCMA_CORE_PCIEG2, "PCIe Gen 2" },
+       { BCMA_CORE_DMA, "DMA" },
+       { BCMA_CORE_SDIO3, "SDIO3" },
+       { BCMA_CORE_USB20, "USB 2.0" },
+       { BCMA_CORE_USB30, "USB 3.0" },
+       { BCMA_CORE_A9JTAG, "ARM Cortex A9 JTAG" },
+       { BCMA_CORE_DDR23, "Denali DDR2/DDR3 memory controller" },
+       { BCMA_CORE_ROM, "ROM" },
+       { BCMA_CORE_NAND, "NAND flash controller" },
+       { BCMA_CORE_QSPI, "SPI flash controller" },
+       { BCMA_CORE_CHIPCOMMON_B, "Chipcommon B" },
+       { BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" },
        { BCMA_CORE_AMEMC, "AMEMC (DDR)" },
        { BCMA_CORE_ALTA, "ALTA (I2S)" },
        { BCMA_CORE_INVALID, "Invalid" },
@@ -201,7 +213,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr)
        return ent;
 }
 
-static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
+static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
                                  u32 type, u8 port)
 {
        u32 addrl, addrh, sizel, sizeh = 0;
@@ -213,7 +225,7 @@ static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
            ((ent & SCAN_ADDR_TYPE) != type) ||
            (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
                bcma_erom_push_ent(eromptr);
-               return -EINVAL;
+               return (u32)-EINVAL;
        }
 
        addrl = ent & SCAN_ADDR_ADDR;
@@ -261,7 +273,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                              struct bcma_device_id *match, int core_num,
                              struct bcma_device *core)
 {
-       s32 tmp;
+       u32 tmp;
        u8 i, j;
        s32 cia, cib;
        u8 ports[2], wrappers[2];
@@ -339,11 +351,11 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
         * the main register space for the core
         */
        tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
-       if (tmp <= 0) {
+       if (tmp == 0 || IS_ERR_VALUE(tmp)) {
                /* Try again to see if it is a bridge */
                tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                              SCAN_ADDR_TYPE_BRIDGE, 0);
-               if (tmp <= 0) {
+               if (tmp == 0 || IS_ERR_VALUE(tmp)) {
                        return -EILSEQ;
                } else {
                        bcma_info(bus, "Bridge found\n");
@@ -357,7 +369,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_SLAVE, i);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: slave port %d "
                                 * "has %d descriptors\n", i, j); */
@@ -374,7 +386,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_MWRAP, i);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: master wrapper %d "
                                 * "has %d descriptors\n", i, j); */
@@ -392,7 +404,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_SWRAP, i + hack);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: master wrapper %d "
                                 * has %d descriptors\n", i, j); */
index 1d680ba..7b839bf 100644 (file)
@@ -130,7 +130,7 @@ config BNX2X_SRIOV
 
 config BGMAC
        tristate "BCMA bus GBit core support"
-       depends on BCMA_HOST_SOC && HAS_DMA
+       depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
        ---help---
          This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
          They can be found on BCM47xx SoCs and provide gigabit ethernet.
index daeafef..e0ba7cd 100644 (file)
@@ -159,7 +159,7 @@ struct ath_common {
 
        bool btcoex_enabled;
        bool disable_ani;
-       bool antenna_diversity;
+       bool bt_ant_diversity;
 };
 
 struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
index 1a2ef51..744da6d 100644 (file)
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+       ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
        struct bmi_cmd cmd;
@@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
                                                  &resp, &rxlen);
                if (ret) {
-                       ath10k_warn("unable to read from the device\n");
+                       ath10k_warn("unable to read from the device (%d)\n",
+                                   ret);
                        return ret;
                }
 
@@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
                                                  NULL, NULL);
                if (ret) {
-                       ath10k_warn("unable to write to the device\n");
+                       ath10k_warn("unable to write to the device (%d)\n",
+                                   ret);
                        return ret;
                }
 
index 32c56aa..8d81ce1 100644 (file)
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
                               struct bmi_target_info *target_info);
index 61a8ac7..b407929 100644 (file)
@@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *indicator_addr;
 
-       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
                ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
                return;
        }
index 2b3426b..7226c23 100644 (file)
@@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
                goto conn_fail;
 
        /* Start HTC */
-       status = ath10k_htc_start(ar->htc);
+       status = ath10k_htc_start(&ar->htc);
        if (status)
                goto conn_fail;
 
@@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
        return 0;
 
 timeout:
-       ath10k_htc_stop(ar->htc);
+       ath10k_htc_stop(&ar->htc);
 conn_fail:
        return status;
 }
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+       const struct firmware *fw = ar->board_data;
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 address;
-       const struct firmware *fw;
        int ret;
 
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.board);
-       if (IS_ERR(fw)) {
-               ath10k_err("could not fetch board data fw file (%ld)\n",
-                          PTR_ERR(fw));
-               return PTR_ERR(fw);
-       }
-
        ret = ath10k_push_board_ext_data(ar, fw);
        if (ret) {
                ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
        return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       const struct firmware *fw;
-       u32 address;
+       const struct firmware *fw = ar->otp;
+       u32 address = ar->hw_params.patch_load_addr;
        u32 exec_param;
        int ret;
 
        /* OTP is optional */
 
-       if (ar->hw_params.fw.otp == NULL) {
-               ath10k_info("otp file not defined\n");
-               return 0;
-       }
-
-       address = ar->hw_params.patch_load_addr;
-
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.otp);
-       if (IS_ERR(fw)) {
-               ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+       if (!ar->otp)
                return 0;
-       }
 
        ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
        if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
        return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-       const struct firmware *fw;
+       const struct firmware *fw = ar->firmware;
        u32 address;
        int ret;
 
-       if (ar->hw_params.fw.fw == NULL)
-               return -EINVAL;
-
        address = ar->hw_params.patch_load_addr;
 
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.fw);
-       if (IS_ERR(fw)) {
-               ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-               return PTR_ERR(fw);
-       }
-
        ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
        if (ret) {
                ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
+       return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+       if (ar->board_data && !IS_ERR(ar->board_data))
+               release_firmware(ar->board_data);
+
+       if (ar->otp && !IS_ERR(ar->otp))
+               release_firmware(ar->otp);
+
+       if (ar->firmware && !IS_ERR(ar->firmware))
+               release_firmware(ar->firmware);
+
+       ar->board_data = NULL;
+       ar->otp = NULL;
+       ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+       int ret = 0;
+
+       if (ar->hw_params.fw.fw == NULL) {
+               ath10k_err("firmware file not defined\n");
+               return -EINVAL;
+       }
+
+       if (ar->hw_params.fw.board == NULL) {
+               ath10k_err("board data file not defined");
+               return -EINVAL;
+       }
+
+       ar->board_data = ath10k_fetch_fw_file(ar,
+                                             ar->hw_params.fw.dir,
+                                             ar->hw_params.fw.board);
+       if (IS_ERR(ar->board_data)) {
+               ret = PTR_ERR(ar->board_data);
+               ath10k_err("could not fetch board data (%d)\n", ret);
+               goto err;
+       }
+
+       ar->firmware = ath10k_fetch_fw_file(ar,
+                                           ar->hw_params.fw.dir,
+                                           ar->hw_params.fw.fw);
+       if (IS_ERR(ar->firmware)) {
+               ret = PTR_ERR(ar->firmware);
+               ath10k_err("could not fetch firmware (%d)\n", ret);
+               goto err;
+       }
+
+       /* OTP may be undefined. If so, don't fetch it at all */
+       if (ar->hw_params.fw.otp == NULL)
+               return 0;
+
+       ar->otp = ath10k_fetch_fw_file(ar,
+                                      ar->hw_params.fw.dir,
+                                      ar->hw_params.fw.otp);
+       if (IS_ERR(ar->otp)) {
+               ret = PTR_ERR(ar->otp);
+               ath10k_err("could not fetch otp (%d)\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       ath10k_core_free_firmware_files(ar);
        return ret;
 }
 
@@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar)
        return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+       mutex_lock(&ar->conf_mutex);
+
+       switch (ar->state) {
+       case ATH10K_STATE_ON:
+               ath10k_halt(ar);
+               ar->state = ATH10K_STATE_RESTARTING;
+               ieee80211_restart_hw(ar->hw);
+               break;
+       case ATH10K_STATE_OFF:
+               /* this can happen if driver is being unloaded */
+               ath10k_warn("cannot restart a device that hasn't been started\n");
+               break;
+       case ATH10K_STATE_RESTARTING:
+       case ATH10K_STATE_RESTARTED:
+               ar->state = ATH10K_STATE_WEDGED;
+               /* fall through */
+       case ATH10K_STATE_WEDGED:
+               ath10k_warn("device is wedged, will not restart\n");
+               break;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 enum ath10k_bus bus,
                                  const struct ath10k_hif_ops *hif_ops)
 {
        struct ath10k *ar;
@@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
        ar->hif.priv = hif_priv;
        ar->hif.ops = hif_ops;
-       ar->hif.bus = bus;
-
-       ar->free_vdev_map = 0xFF; /* 8 vdevs */
 
        init_completion(&ar->scan.started);
        init_completion(&ar->scan.completed);
@@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
        init_waitqueue_head(&ar->event_queue);
 
+       INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
        return ar;
 
 err_wq:
@@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
-       struct ath10k_htc_ops htc_ops;
-       struct bmi_target_info target_info;
        int status;
 
-       memset(&target_info, 0, sizeof(target_info));
-       status = ath10k_bmi_get_target_info(ar, &target_info);
-       if (status)
-               goto err;
-
-       ar->target_version = target_info.version;
-       ar->hw->wiphy->hw_version = target_info.version;
-
-       status = ath10k_init_hw_params(ar);
-       if (status)
-               goto err;
+       ath10k_bmi_start(ar);
 
        if (ath10k_init_configure_target(ar)) {
                status = -EINVAL;
@@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar)
        if (status)
                goto err;
 
-       htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+       ar->htc.htc_ops.target_send_suspend_complete =
+               ath10k_send_suspend_complete;
 
-       ar->htc = ath10k_htc_create(ar, &htc_ops);
-       if (IS_ERR(ar->htc)) {
-               status = PTR_ERR(ar->htc);
-               ath10k_err("could not create HTC (%d)\n", status);
+       status = ath10k_htc_init(ar);
+       if (status) {
+               ath10k_err("could not init HTC (%d)\n", status);
                goto err;
        }
 
        status = ath10k_bmi_done(ar);
        if (status)
-               goto err_htc_destroy;
+               goto err;
 
        status = ath10k_wmi_attach(ar);
        if (status) {
                ath10k_err("WMI attach failed: %d\n", status);
-               goto err_htc_destroy;
+               goto err;
        }
 
-       status = ath10k_htc_wait_target(ar->htc);
+       status = ath10k_htc_wait_target(&ar->htc);
        if (status)
                goto err_wmi_detach;
 
-       ar->htt = ath10k_htt_attach(ar);
-       if (!ar->htt) {
-               status = -ENOMEM;
+       status = ath10k_htt_attach(ar);
+       if (status) {
+               ath10k_err("could not attach htt (%d)\n", status);
                goto err_wmi_detach;
        }
 
@@ -588,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar)
                goto err_disconnect_htc;
        }
 
-       status = ath10k_htt_attach_target(ar->htt);
-       if (status)
-               goto err_disconnect_htc;
-
-       status = ath10k_mac_register(ar);
+       status = ath10k_htt_attach_target(&ar->htt);
        if (status)
                goto err_disconnect_htc;
 
-       status = ath10k_debug_create(ar);
-       if (status) {
-               ath10k_err("unable to initialize debugfs\n");
-               goto err_unregister_mac;
-       }
+       ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
 
        return 0;
 
-err_unregister_mac:
-       ath10k_mac_unregister(ar);
 err_disconnect_htc:
-       ath10k_htc_stop(ar->htc);
+       ath10k_htc_stop(&ar->htc);
 err_htt_detach:
-       ath10k_htt_detach(ar->htt);
+       ath10k_htt_detach(&ar->htt);
 err_wmi_detach:
        ath10k_wmi_detach(ar);
-err_htc_destroy:
-       ath10k_htc_destroy(ar->htc);
 err:
        return status;
 }
-EXPORT_SYMBOL(ath10k_core_register);
+EXPORT_SYMBOL(ath10k_core_start);
 
-void ath10k_core_unregister(struct ath10k *ar)
+void ath10k_core_stop(struct ath10k *ar)
 {
-       /* We must unregister from mac80211 before we stop HTC and HIF.
-        * Otherwise we will fail to submit commands to FW and mac80211 will be
-        * unhappy about callback failures. */
-       ath10k_mac_unregister(ar);
-       ath10k_htc_stop(ar->htc);
-       ath10k_htt_detach(ar->htt);
+       ath10k_htc_stop(&ar->htc);
+       ath10k_htt_detach(&ar->htt);
        ath10k_wmi_detach(ar);
-       ath10k_htc_destroy(ar->htc);
 }
-EXPORT_SYMBOL(ath10k_core_unregister);
+EXPORT_SYMBOL(ath10k_core_stop);
 
-int ath10k_core_target_suspend(struct ath10k *ar)
+/* mac80211 manages fw/hw initialization through start/stop hooks. However in
+ * order to know what hw capabilities should be advertised to mac80211 it is
+ * necessary to load the firmware (and tear it down immediately since start
+ * hook will try to init it again) before registering */
+static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-       int ret;
+       struct bmi_target_info target_info;
+       int ret = 0;
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not start pci hif (%d)\n", ret);
+               return ret;
+       }
 
-       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+       memset(&target_info, 0, sizeof(target_info));
+       ret = ath10k_bmi_get_target_info(ar, &target_info);
+       if (ret) {
+               ath10k_err("could not get target info (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
 
-       ret = ath10k_wmi_pdev_suspend_target(ar);
-       if (ret)
-               ath10k_warn("could not suspend target (%d)\n", ret);
+       ar->target_version = target_info.version;
+       ar->hw->wiphy->hw_version = target_info.version;
 
-       return ret;
+       ret = ath10k_init_hw_params(ar);
+       if (ret) {
+               ath10k_err("could not get hw params (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ret = ath10k_core_fetch_firmware_files(ar);
+       if (ret) {
+               ath10k_err("could not fetch firmware files (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_core_free_firmware_files(ar);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+       return 0;
 }
-EXPORT_SYMBOL(ath10k_core_target_suspend);
 
-int ath10k_core_target_resume(struct ath10k *ar)
+int ath10k_core_register(struct ath10k *ar)
 {
-       int ret;
+       int status;
 
-       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+       status = ath10k_core_probe_fw(ar);
+       if (status) {
+               ath10k_err("could not probe fw (%d)\n", status);
+               return status;
+       }
 
-       ret = ath10k_wmi_pdev_resume_target(ar);
-       if (ret)
-               ath10k_warn("could not resume target (%d)\n", ret);
+       status = ath10k_mac_register(ar);
+       if (status) {
+               ath10k_err("could not register to mac80211 (%d)\n", status);
+               goto err_release_fw;
+       }
 
-       return ret;
+       status = ath10k_debug_create(ar);
+       if (status) {
+               ath10k_err("unable to initialize debugfs\n");
+               goto err_unregister_mac;
+       }
+
+       return 0;
+
+err_unregister_mac:
+       ath10k_mac_unregister(ar);
+err_release_fw:
+       ath10k_core_free_firmware_files(ar);
+       return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+       /* We must unregister from mac80211 before we stop HTC and HIF.
+        * Otherwise we will fail to submit commands to FW and mac80211 will be
+        * unhappy about callback failures. */
+       ath10k_mac_unregister(ar);
+       ath10k_core_free_firmware_files(ar);
 }
-EXPORT_SYMBOL(ath10k_core_target_resume);
+EXPORT_SYMBOL(ath10k_core_unregister);
 
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
index 539336d..9f21ecb 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 
+#include "htt.h"
 #include "htc.h"
 #include "hw.h"
 #include "targaddrs.h"
 
 struct ath10k;
 
-enum ath10k_bus {
-       ATH10K_BUS_PCI,
-};
-
 struct ath10k_skb_cb {
        dma_addr_t paddr;
        bool is_mapped;
@@ -250,6 +247,28 @@ struct ath10k_debug {
        struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+       ATH10K_STATE_OFF = 0,
+       ATH10K_STATE_ON,
+
+       /* When doing firmware recovery the device is first powered down.
+        * mac80211 is supposed to call in to start() hook later on. It is
+        * however possible that driver unloading and firmware crash overlap.
+        * mac80211 can wait on conf_mutex in stop() while the device is
+        * stopped in ath10k_core_restart() work holding conf_mutex. The state
+        * RESTARTED means that the device is up and mac80211 has started hw
+        * reconfiguration. Once mac80211 is done with the reconfiguration we
+        * set the state to STATE_ON in restart_complete(). */
+       ATH10K_STATE_RESTARTING,
+       ATH10K_STATE_RESTARTED,
+
+       /* The device has crashed while restarting hw. This state is like ON
+        * but commands are blocked in HTC and -ECOMM response is given. This
+        * prevents completion timeouts and makes the driver more responsive to
+        * userspace commands. This is also prevents recursive recovery. */
+       ATH10K_STATE_WEDGED,
+};
+
 struct ath10k {
        struct ath_common ath_common;
        struct ieee80211_hw *hw;
@@ -274,19 +293,16 @@ struct ath10k {
 
        struct {
                void *priv;
-               enum ath10k_bus bus;
                const struct ath10k_hif_ops *ops;
        } hif;
 
-       struct ath10k_wmi wmi;
-
        wait_queue_head_t event_queue;
        bool is_target_paused;
 
        struct ath10k_bmi bmi;
-
-       struct ath10k_htc *htc;
-       struct ath10k_htt *htt;
+       struct ath10k_wmi wmi;
+       struct ath10k_htc htc;
+       struct ath10k_htt htt;
 
        struct ath10k_hw_params {
                u32 id;
@@ -301,6 +317,10 @@ struct ath10k {
                } fw;
        } hw_params;
 
+       const struct firmware *board_data;
+       const struct firmware *otp;
+       const struct firmware *firmware;
+
        struct {
                struct completion started;
                struct completion completed;
@@ -350,20 +370,22 @@ struct ath10k {
        struct completion offchan_tx_completed;
        struct sk_buff *offchan_tx_skb;
 
+       enum ath10k_state state;
+
+       struct work_struct restart_work;
+
 #ifdef CONFIG_ATH10K_DEBUGFS
        struct ath10k_debug debug;
 #endif
 };
 
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 enum ath10k_bus bus,
                                  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-int ath10k_core_target_suspend(struct ath10k *ar);
-int ath10k_core_target_resume(struct ath10k *ar);
-
 #endif /* _CORE_H_ */
index 499034b..3d65594 100644 (file)
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        struct wmi_pdev_stats *ps;
        int i;
 
-       mutex_lock(&ar->conf_mutex);
+       spin_lock_bh(&ar->data_lock);
 
        stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
                }
        }
 
+       spin_unlock_bh(&ar->data_lock);
        mutex_unlock(&ar->conf_mutex);
        complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
        struct ath10k *ar = file->private_data;
        struct ath10k_target_stats *fw_stats;
-       char *buf;
+       char *buf = NULL;
        unsigned int len = 0, buf_len = 2500;
-       ssize_t ret_cnt;
+       ssize_t ret_cnt = 0;
        long left;
        int i;
        int ret;
 
        fw_stats = &ar->debug.target_stats;
 
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_ON)
+               goto exit;
+
        buf = kzalloc(buf_len, GFP_KERNEL);
        if (!buf)
-               return -ENOMEM;
+               goto exit;
 
        ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
        if (ret) {
                ath10k_warn("could not request stats (%d)\n", ret);
-               kfree(buf);
-               return -EIO;
+               goto exit;
        }
 
        left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+       if (left <= 0)
+               goto exit;
 
-       if (left <= 0) {
-               kfree(buf);
-               return -ETIMEDOUT;
-       }
-
-       mutex_lock(&ar->conf_mutex);
-
+       spin_lock_bh(&ar->data_lock);
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s\n",
                         "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                                 fw_stats->peer_stat[i].peer_tx_rate);
                len += scnprintf(buf + len, buf_len - len, "\n");
        }
+       spin_unlock_bh(&ar->data_lock);
 
        if (len > buf_len)
                len = buf_len;
 
        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
        mutex_unlock(&ar->conf_mutex);
-
        kfree(buf);
        return ret_cnt;
 }
@@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       const char buf[] = "To simulate firmware crash write the keyword"
+                          " `crash` to this file.\nThis will force firmware"
+                          " to report a crash to the host system.\n";
+       return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char buf[32] = {};
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+       if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_RESTARTED) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       ath10k_info("simulating firmware crash\n");
+
+       ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+       if (ret)
+               ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+       if (ret == 0)
+               ret = count;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+       .read = ath10k_read_simulate_fw_crash,
+       .write = ath10k_write_simulate_fw_crash,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
        ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
                            &fops_wmi_services);
 
+       debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+                           ar, &fops_simulate_fw_crash);
+
        return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
index 73a24d4..dcdea68 100644 (file)
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
                                void *request, u32 request_len,
                                void *response, u32 *response_len);
 
+       /* Post BMI phase, after FW is loaded. Starts regular operation */
        int (*start)(struct ath10k *ar);
 
+       /* Clean up what start() did. This does not revert to BMI phase. If
+        * desired so, call power_down() and power_up() */
        void (*stop)(struct ath10k *ar);
 
        int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -66,10 +69,20 @@ struct ath10k_hif_ops {
         */
        void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
 
-       void (*init)(struct ath10k *ar,
-                    struct ath10k_hif_cb *callbacks);
+       void (*set_callbacks)(struct ath10k *ar,
+                             struct ath10k_hif_cb *callbacks);
 
        u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+       /* Power up the device and enter BMI transfer mode for FW download */
+       int (*power_up)(struct ath10k *ar);
+
+       /* Power down the device and free up resources. stop() must be called
+        * before this if start() was called earlier */
+       void (*power_down)(struct ath10k *ar);
+
+       int (*suspend)(struct ath10k *ar);
+       int (*resume)(struct ath10k *ar);
 };
 
 
@@ -122,10 +135,10 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
        ar->hif.ops->send_complete_check(ar, pipe_id, force);
 }
 
-static inline void ath10k_hif_init(struct ath10k *ar,
-                                  struct ath10k_hif_cb *callbacks)
+static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
+                                           struct ath10k_hif_cb *callbacks)
 {
-       ar->hif.ops->init(ar, callbacks);
+       ar->hif.ops->set_callbacks(ar, callbacks);
 }
 
 static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
@@ -134,4 +147,30 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
        return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+       return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+       ar->hif.ops->power_down(ar);
+}
+
+static inline int ath10k_hif_suspend(struct ath10k *ar)
+{
+       if (!ar->hif.ops->suspend)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->suspend(ar);
+}
+
+static inline int ath10k_hif_resume(struct ath10k *ar)
+{
+       if (!ar->hif.ops->resume)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->resume(ar);
+}
+
 #endif /* _HIF_H_ */
index 74363c9..ef3329e 100644 (file)
@@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+       if (htc->ar->state == ATH10K_STATE_WEDGED)
+               return -ECOMM;
+
        if (eid >= ATH10K_HTC_EP_COUNT) {
                ath10k_warn("Invalid endpoint id: %d\n", eid);
                return -ENOENT;
        }
 
-       skb_push(skb, sizeof(struct ath10k_htc_hdr));
-
        spin_lock_bh(&htc->tx_lock);
+       if (htc->stopped) {
+               spin_unlock_bh(&htc->tx_lock);
+               return -ESHUTDOWN;
+       }
+
        __skb_queue_tail(&ep->tx_queue, skb);
+       skb_push(skb, sizeof(struct ath10k_htc_hdr));
        spin_unlock_bh(&htc->tx_lock);
 
        queue_work(htc->ar->workqueue, &ep->send_work);
@@ -265,25 +272,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
                                            struct sk_buff *skb,
                                            unsigned int eid)
 {
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
-       bool stopping;
 
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
 
+       /* note: when using TX credit flow, the re-checking of queues happens
+        * when credits flow back from the target.  in the non-TX credit case,
+        * we recheck after the packet completes */
        spin_lock_bh(&htc->tx_lock);
-       stopping = htc->stopping;
-       spin_unlock_bh(&htc->tx_lock);
-
-       if (!ep->tx_credit_flow_enabled && !stopping)
-               /*
-                * note: when using TX credit flow, the re-checking of
-                * queues happens when credits flow back from the target.
-                * in the non-TX credit case, we recheck after the packet
-                * completes
-                */
+       if (!ep->tx_credit_flow_enabled && !htc->stopped)
                queue_work(ar->workqueue, &ep->send_work);
+       spin_unlock_bh(&htc->tx_lock);
 
        return 0;
 }
@@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                                            u8 pipe_id)
 {
        int status = 0;
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_hdr *hdr;
        struct ath10k_htc_ep *ep;
        u16 payload_len;
@@ -751,8 +752,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
        tx_alloc = ath10k_htc_get_credit_allocation(htc,
                                                    conn_req->service_id);
        if (!tx_alloc)
-               ath10k_warn("HTC Service %s does not allocate target credits\n",
-                           htc_service_name(conn_req->service_id));
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC Service %s does not allocate target credits\n",
+                          htc_service_name(conn_req->service_id));
 
        skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
        if (!skb) {
@@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        struct ath10k_htc_ep *ep;
 
        spin_lock_bh(&htc->tx_lock);
-       htc->stopping = true;
+       htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
 
        for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
@@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        }
 
        ath10k_hif_stop(htc->ar);
-       ath10k_htc_reset_endpoint_states(htc);
 }
 
 /* registered target arrival callback from the HIF layer */
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-                                    struct ath10k_htc_ops *htc_ops)
+int ath10k_htc_init(struct ath10k *ar)
 {
        struct ath10k_hif_cb htc_callbacks;
        struct ath10k_htc_ep *ep = NULL;
-       struct ath10k_htc *htc = NULL;
-
-       /* FIXME: use struct ath10k instead */
-       htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
-       if (!htc)
-               return ERR_PTR(-ENOMEM);
+       struct ath10k_htc *htc = &ar->htc;
 
        spin_lock_init(&htc->tx_lock);
 
-       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
-
+       htc->stopped = false;
        ath10k_htc_reset_endpoint_states(htc);
 
        /* setup HIF layer callbacks */
@@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
        /* Get HIF default pipe for HTC message exchange */
        ep = &htc->endpoint[ATH10K_HTC_EP_0];
 
-       ath10k_hif_init(ar, &htc_callbacks);
+       ath10k_hif_set_callbacks(ar, &htc_callbacks);
        ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
 
        init_completion(&htc->ctl_resp);
 
-       return htc;
-}
-
-void ath10k_htc_destroy(struct ath10k_htc *htc)
-{
-       kfree(htc);
+       return 0;
 }
index fa45844..e1dd8c7 100644 (file)
@@ -335,7 +335,7 @@ struct ath10k_htc {
        struct ath10k *ar;
        struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
 
-       /* protects endpoint and stopping fields */
+       /* protects endpoint and stopped fields */
        spinlock_t tx_lock;
 
        struct ath10k_htc_ops htc_ops;
@@ -349,11 +349,10 @@ struct ath10k_htc {
        struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
        int target_credit_size;
 
-       bool stopping;
+       bool stopped;
 };
 
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-                                    struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_init(struct ath10k *ar);
 int ath10k_htc_wait_target(struct ath10k_htc *htc);
 int ath10k_htc_start(struct ath10k_htc *htc);
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
@@ -362,7 +361,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
                    struct sk_buff *packet);
 void ath10k_htc_stop(struct ath10k_htc *htc);
-void ath10k_htc_destroy(struct ath10k_htc *htc);
 struct sk_buff *ath10k_htc_alloc_skb(int size);
 
 #endif
index 185a546..39342c5 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/if_ether.h>
 
 #include "htt.h"
 #include "core.h"
@@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        /* connect to control service */
        conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
 
-       status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+       status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
                                            &conn_resp);
 
        if (status)
@@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_attach(struct ath10k *ar)
 {
-       struct ath10k_htt *htt;
+       struct ath10k_htt *htt = &ar->htt;
        int ret;
 
-       htt = kzalloc(sizeof(*htt), GFP_KERNEL);
-       if (!htt)
-               return NULL;
-
        htt->ar = ar;
        htt->max_throughput_mbps = 800;
 
@@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
         * since ath10k_htt_rx_attach involves sending a rx ring configure
         * message to the target.
         */
-       if (ath10k_htt_htc_attach(htt))
+       ret = ath10k_htt_htc_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt htc (%d)\n", ret);
                goto err_htc_attach;
+       }
 
        ret = ath10k_htt_tx_attach(htt);
        if (ret) {
@@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
                goto err_htc_attach;
        }
 
-       if (ath10k_htt_rx_attach(htt))
+       ret = ath10k_htt_rx_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt rx (%d)\n", ret);
                goto err_rx_attach;
+       }
 
        /*
         * Prefetch enough data to satisfy target
@@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
                8 + /* llc snap */
                2; /* ip4 dscp or ip6 priority */
 
-       return htt;
+       return 0;
 
 err_rx_attach:
        ath10k_htt_tx_detach(htt);
 err_htc_attach:
-       kfree(htt);
-       return NULL;
+       return ret;
 }
 
 #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
@@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt)
 {
        ath10k_htt_rx_detach(htt);
        ath10k_htt_tx_detach(htt);
-       kfree(htt);
 }
index a7a7aa0..318be46 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <linux/bug.h>
 
-#include "core.h"
 #include "htc.h"
 #include "rx_desc.h"
 
@@ -1317,7 +1316,7 @@ struct htt_rx_desc {
 #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
 #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach(struct ath10k *ar);
 int ath10k_htt_attach_target(struct ath10k_htt *htt);
 void ath10k_htt_detach(struct ath10k_htt *htt);
 
index de058d7..04f08d9 100644 (file)
@@ -15,6 +15,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "core.h"
 #include "htc.h"
 #include "htt.h"
 #include "txrx.h"
@@ -1036,7 +1037,7 @@ end:
 
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 {
-       struct ath10k_htt *htt = ar->htt;
+       struct ath10k_htt *htt = &ar->htt;
        struct htt_resp *resp = (struct htt_resp *)skb->data;
 
        /* confirm alignment */
index ef79106..dc3f3e8 100644 (file)
@@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
 
        /* At the beginning free queue number should hint us the maximum
         * queue length */
-       pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+       pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id;
        htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
                                                                   pipe);
 
@@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
-       struct ath10k_htt *htt = ar->htt;
+       struct ath10k_htt *htt = &ar->htt;
 
        if (skb_cb->htt.is_conf) {
                dev_kfree_skb_any(skb);
@@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
 
        ATH10K_SKB_CB(skb)->htt.is_conf = true;
 
-       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
                dev_kfree_skb_any(skb);
                return ret;
@@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
 
        ATH10K_SKB_CB(skb)->htt.is_conf = true;
 
-       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
                dev_kfree_skb_any(skb);
                return ret;
@@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        skb_cb->htt.refcount = 2;
        skb_cb->htt.msdu = msdu;
 
-       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
                goto err;
 
@@ -486,7 +486,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        skb_cb->htt.txfrag = txfrag;
        skb_cb->htt.msdu = msdu;
 
-       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
                goto err;
 
index da5c333..d0a7761 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                .macaddr = macaddr,
        };
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
                arg.key_flags = WMI_KEY_PAIRWISE;
        else
@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
        struct ath10k *ar = arvif->ar;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        INIT_COMPLETION(ar->install_key_done);
 
        ret = ath10k_send_key(arvif, key, cmd, macaddr);
@@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        return 0;
 }
 
+static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
+                             ATH10K_RTS_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                        value);
+}
+
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
+                               ATH10K_FRAGMT_THRESHOLD_MIN,
+                               ATH10K_FRAGMT_THRESHOLD_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                        value);
+}
+
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
        int ret;
@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
        spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+       struct ath10k_peer *peer, *tmp;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = wait_for_completion_timeout(&ar->vdev_setup_done,
                                          ATH10K_VDEV_SETUP_TIMEOUT_HZ);
        if (ret == 0)
@@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
                return;
@@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
@@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        enum wmi_sta_ps_mode psmode;
        int ret;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
@@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
                                      struct ieee80211_bss_conf *bss_conf,
                                      struct wmi_peer_assoc_complete_arg *arg)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
        memcpy(arg->addr, sta->addr, ETH_ALEN);
        arg->vdev_id = arvif->vdev_id;
        arg->peer_aid = sta->aid;
@@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
        const u8 *rsnie = NULL;
        const u8 *wpaie = NULL;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
                               info->bssid, NULL, 0, 0, 0);
        if (bss) {
@@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
        u32 ratemask;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
        ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
        rates = sband->bitrates;
@@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        int smps;
        int i, n;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (!ht_cap->ht_supported)
                return;
 
@@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
        u32 uapsd = 0;
        u32 max_sp = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (sta->wme)
                arg->peer_flags |= WMI_PEER_QOS;
 
@@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
 {
        struct wmi_peer_assoc_complete_arg arg;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
 
        ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
@@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        struct ieee80211_sta *ap_sta;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        rcu_read_lock();
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        /*
         * For some reason, calling VDEV-DOWN before VDEV-STOP
         * makes the FW to send frames via HTT after disassociation.
@@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
        if (ret) {
                ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
@@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
                ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
@@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        int ret;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bands = hw->wiphy->bands;
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (!bands[band])
@@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-                               struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct reg_dmn_pair_mapping *regpair;
-       struct ath10k *ar = hw->priv;
        int ret;
 
-       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+       lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
                ath10k_warn("could not update channel list (%d)\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 }
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ath10k *ar = hw->priv;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON)
+               ath10k_regd_update(ar);
+       mutex_unlock(&ar->conf_mutex);
+}
+
 /***************/
 /* TX handlers */
 /***************/
@@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
        int ret;
 
        if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else if (ieee80211_is_nullfunc(hdr->frame_control))
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
                 * those frames when it detects link/beacon loss and depends on
                 * the tx status to be correct. */
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else
-               ret = ath10k_htt_tx(ar->htt, skb);
+               ret = ath10k_htt_tx(&ar->htt, skb);
 
        if (ret) {
                ath10k_warn("tx failed (%d). dropping packet.\n", ret);
@@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
                ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               spin_unlock_bh(&ar->data_lock);
                return -EIO;
        }
 
@@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
                tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
        }
 
-       ath10k_tx_h_qos_workaround(hw, control, skb);
-       ath10k_tx_h_update_wep_key(skb);
-       ath10k_tx_h_add_p2p_noa_ie(ar, skb);
-       ath10k_tx_h_seq_no(skb);
+       /* it makes no sense to process injected frames like that */
+       if (info->control.vif &&
+           info->control.vif->type != NL80211_IFTYPE_MONITOR) {
+               ath10k_tx_h_qos_workaround(hw, control, skb);
+               ath10k_tx_h_update_wep_key(skb);
+               ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+               ath10k_tx_h_seq_no(skb);
+       }
 
        memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
        ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
@@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
+void ath10k_halt(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+       ath10k_offchan_tx_purge(ar);
+       ath10k_peer_cleanup_all(ar);
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               del_timer(&ar->scan.timeout);
+               ar->scan.in_progress = false;
+               ieee80211_scan_completed(ar->hw, true);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
-       int ret;
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_OFF &&
+           ar->state != ATH10K_STATE_RESTARTING) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not init hif (%d)\n", ret);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       if (ar->state == ATH10K_STATE_OFF)
+               ar->state = ATH10K_STATE_ON;
+       else if (ar->state == ATH10K_STATE_RESTARTING)
+               ar->state = ATH10K_STATE_RESTARTED;
 
        ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
        if (ret)
@@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
+       ath10k_regd_update(ar);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
        return 0;
 }
 
@@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
-       /* avoid leaks in case FW never confirms scan for offchannel */
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON ||
+           ar->state == ATH10K_STATE_RESTARTED ||
+           ar->state == ATH10K_STATE_WEDGED)
+               ath10k_halt(ar);
+
+       ar->state = ATH10K_STATE_OFF;
+       mutex_unlock(&ar->conf_mutex);
+
        cancel_work_sync(&ar->offchan_tx_work);
-       ath10k_offchan_tx_purge(ar);
+       cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
        struct ath10k_generic_iter ar_iter;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       ieee80211_iterate_active_interfaces_atomic(
+               ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_ps_iter, &ar_iter);
+
+       if (ar_iter.ret)
+               ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
        struct ath10k *ar = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
        int ret = 0;
-       u32 flags;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                spin_unlock_bh(&ar->data_lock);
        }
 
-       if (changed & IEEE80211_CONF_CHANGE_PS) {
-               memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-               ar_iter.ar = ar;
-               flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-               ieee80211_iterate_active_interfaces_atomic(hw,
-                                                          flags,
-                                                          ath10k_ps_iter,
-                                                          &ar_iter);
-
-               ret = ar_iter.ret;
-       }
+       if (changed & IEEE80211_CONF_CHANGE_PS)
+               ath10k_config_ps(ar);
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
                if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                        ret = ath10k_monitor_destroy(ar);
        }
 
+       ath10k_wmi_flush_tx(ar);
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -1859,6 +2023,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
        }
 
+       ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
+       if (ret)
+               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
+       ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
+       if (ret)
+               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
@@ -2363,6 +2537,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
        u32 value = 0;
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
                return 0;
 
@@ -2558,11 +2734,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
 
-       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
-                                                rts);
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
        if (ar_iter->ret)
                ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
                            arvif->vdev_id);
@@ -2581,8 +2762,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_rts_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_rts_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2593,17 +2775,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_generic_iter *ar_iter = data;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-       int ret;
 
-       frag = clamp_t(u32, frag,
-                      ATH10K_FRAGMT_THRESHOLD_MIN,
-                      ATH10K_FRAGMT_THRESHOLD_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-                                       frag);
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
 
-       ar_iter->ret = ret;
+       ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
        if (ar_iter->ret)
                ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
                            arvif->vdev_id);
@@ -2622,8 +2804,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_frag_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_frag_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2632,6 +2815,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath10k *ar = hw->priv;
+       bool skip;
        int ret;
 
        /* mac80211 doesn't care if we really xmit queued frames or not
@@ -2639,16 +2823,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
        if (drop)
                return;
 
-       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state == ATH10K_STATE_WEDGED)
+               goto skip;
+
+       ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
                        bool empty;
-                       spin_lock_bh(&ar->htt->tx_lock);
-                       empty = bitmap_empty(ar->htt->used_msdu_ids,
-                                            ar->htt->max_num_pending_tx);
-                       spin_unlock_bh(&ar->htt->tx_lock);
-                       (empty);
+
+                       spin_lock_bh(&ar->htt.tx_lock);
+                       empty = bitmap_empty(ar->htt.used_msdu_ids,
+                                            ar->htt.max_num_pending_tx);
+                       spin_unlock_bh(&ar->htt.tx_lock);
+
+                       skip = (ar->state == ATH10K_STATE_WEDGED);
+
+                       (empty || skip);
                }), ATH10K_FLUSH_TIMEOUT_HZ);
-       if (ret <= 0)
+
+       if (ret <= 0 || skip)
                ath10k_warn("tx not flushed\n");
+
+skip:
+       mutex_unlock(&ar->conf_mutex);
 }
 
 /* TODO: Implement this function properly
@@ -2660,6 +2857,83 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
        return 1;
 }
 
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+                         struct cfg80211_wowlan *wowlan)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ar->is_target_paused = false;
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret) {
+               ath10k_warn("could not suspend target (%d)\n", ret);
+               return 1;
+       }
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                              ar->is_target_paused == true,
+                                              1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               goto resume;
+       }
+
+       ret = ath10k_hif_suspend(ar);
+       if (ret) {
+               ath10k_warn("could not suspend hif (%d)\n", ret);
+               goto resume;
+       }
+
+       return 0;
+resume:
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+       return 1;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_hif_resume(ar);
+       if (ret) {
+               ath10k_warn("could not resume hif (%d)\n", ret);
+               return 1;
+       }
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret) {
+               ath10k_warn("could not resume target (%d)\n", ret);
+               return 1;
+       }
+
+       return 0;
+}
+#endif
+
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+
+       /* If device failed to restart it will be in a different state, e.g.
+        * ATH10K_STATE_WEDGED */
+       if (ar->state == ATH10K_STATE_RESTARTED) {
+               ath10k_info("device successfully recovered\n");
+               ar->state = ATH10K_STATE_ON;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -2680,6 +2954,11 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+       .restart_complete               = ath10k_restart_complete,
+#ifdef CONFIG_PM
+       .suspend                        = ath10k_suspend,
+       .resume                         = ath10k_resume,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -2948,8 +3227,10 @@ int ath10k_mac_register(struct ath10k *ar)
                channels = kmemdup(ath10k_2ghz_channels,
                                   sizeof(ath10k_2ghz_channels),
                                   GFP_KERNEL);
-               if (!channels)
-                       return -ENOMEM;
+               if (!channels) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
 
                band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
                band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
@@ -2968,11 +3249,8 @@ int ath10k_mac_register(struct ath10k *ar)
                                   sizeof(ath10k_5ghz_channels),
                                   GFP_KERNEL);
                if (!channels) {
-                       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
-                               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
-                               kfree(band->channels);
-                       }
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_free;
                }
 
                band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
@@ -3036,25 +3314,30 @@ int ath10k_mac_register(struct ath10k *ar)
                            ath10k_reg_notifier);
        if (ret) {
                ath10k_err("Regulatory initialization failed\n");
-               return ret;
+               goto err_free;
        }
 
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
                ath10k_err("ieee80211 registration failed: %d\n", ret);
-               return ret;
+               goto err_free;
        }
 
        if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
                ret = regulatory_hint(ar->hw->wiphy,
                                      ar->ath_common.regulatory.alpha2);
                if (ret)
-                       goto exit;
+                       goto err_unregister;
        }
 
        return 0;
-exit:
+
+err_unregister:
        ieee80211_unregister_hw(ar->hw);
+err_free:
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
        return ret;
 }
 
index 27fc92e..6fce9bf 100644 (file)
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
index 33af467..c71b488 100644 (file)
@@ -54,6 +54,8 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
                                             int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
        /* host->target HTC control and raw streams */
@@ -718,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
                           reg_dump_values[i + 1],
                           reg_dump_values[i + 2],
                           reg_dump_values[i + 3]);
+
+       ieee80211_queue_work(ar->hw, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
@@ -744,8 +748,8 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
        ath10k_ce_per_engine_service(ar, pipe);
 }
 
-static void ath10k_pci_hif_post_init(struct ath10k *ar,
-                                    struct ath10k_hif_cb *callbacks)
+static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
+                                        struct ath10k_hif_cb *callbacks)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
@@ -1263,7 +1267,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
        ath10k_pci_process_ce(ar);
        ath10k_pci_cleanup_ce(ar);
        ath10k_pci_buffer_cleanup(ar);
-       ath10k_pci_ce_deinit(ar);
 }
 
 static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
@@ -1735,6 +1738,115 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+       int ret;
+
+       /*
+        * Bring the target up cleanly.
+        *
+        * The target may be in an undefined state with an AUX-powered Target
+        * and a Host in WoW mode. If the Host crashes, loses power, or is
+        * restarted (without unloading the driver) then the Target is left
+        * (aux) powered and running. On a subsequent driver load, the Target
+        * is in an unexpected state. We try to catch that here in order to
+        * reset the Target and retry the probe.
+        */
+       ath10k_pci_device_reset(ar);
+
+       ret = ath10k_pci_reset_target(ar);
+       if (ret)
+               goto err;
+
+       if (ath10k_target_ps) {
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+       } else {
+               /* Force AWAKE forever */
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+               ath10k_do_pci_wake(ar);
+       }
+
+       ret = ath10k_pci_ce_init(ar);
+       if (ret)
+               goto err_ps;
+
+       ret = ath10k_pci_init_config(ar);
+       if (ret)
+               goto err_ce;
+
+       ret = ath10k_pci_wake_target_cpu(ar);
+       if (ret) {
+               ath10k_err("could not wake up target CPU (%d)\n", ret);
+               goto err_ce;
+       }
+
+       return 0;
+
+err_ce:
+       ath10k_pci_ce_deinit(ar);
+err_ps:
+       if (!ath10k_target_ps)
+               ath10k_do_pci_sleep(ar);
+err:
+       return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+       ath10k_pci_ce_deinit(ar);
+       if (!ath10k_target_ps)
+               ath10k_do_pci_sleep(ar);
+}
+
+#ifdef CONFIG_PM
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_hif_suspend(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct pci_dev *pdev = ar_pci->pdev;
+       u32 val;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0x3) {
+               pci_save_state(pdev);
+               pci_disable_device(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      (val & 0xffffff00) | 0x03);
+       }
+
+       return 0;
+}
+
+static int ath10k_pci_hif_resume(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct pci_dev *pdev = ar_pci->pdev;
+       u32 val;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0) {
+               pci_restore_state(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      val & 0xffffff00);
+               /*
+                * 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_read_config_dword(pdev, 0x40, &val);
+
+               if ((val & 0x0000ff00) != 0)
+                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+       }
+
+       return 0;
+}
+#endif
+
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .send_head              = ath10k_pci_hif_send_head,
        .exchange_bmi_msg       = ath10k_pci_hif_exchange_bmi_msg,
@@ -1743,8 +1855,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .map_service_to_pipe    = ath10k_pci_hif_map_service_to_pipe,
        .get_default_pipe       = ath10k_pci_hif_get_default_pipe,
        .send_complete_check    = ath10k_pci_hif_send_complete_check,
-       .init                   = ath10k_pci_hif_post_init,
+       .set_callbacks          = ath10k_pci_hif_set_callbacks,
        .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
+       .power_up               = ath10k_pci_hif_power_up,
+       .power_down             = ath10k_pci_hif_power_down,
+#ifdef CONFIG_PM
+       .suspend                = ath10k_pci_hif_suspend,
+       .resume                 = ath10k_pci_hif_resume,
+#endif
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -2059,9 +2177,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar)
        return 0;
 }
 
-static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+static void ath10k_pci_device_reset(struct ath10k *ar)
 {
-       struct ath10k *ar = ar_pci->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *mem = ar_pci->mem;
        int i;
        u32 val;
@@ -2118,7 +2236,7 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
                case ATH10K_PCI_FEATURE_MSI_X:
                        ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
                        break;
-               case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+               case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND:
                        ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
                        break;
                }
@@ -2145,7 +2263,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        switch (pci_dev->device) {
        case QCA988X_1_0_DEVICE_ID:
-               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features);
                break;
        case QCA988X_2_0_DEVICE_ID:
                set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
@@ -2158,8 +2276,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        ath10k_pci_dump_features(ar_pci);
 
-       ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
-                               &ath10k_pci_hif_ops);
+       ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
        if (!ar) {
                ath10k_err("ath10k_core_create failed!\n");
                ret = -EINVAL;
@@ -2167,7 +2284,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        }
 
        /* Enable QCA988X_1.0 HW workarounds */
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features))
                spin_lock_init(&ar_pci->hw_v1_workaround_lock);
 
        ar_pci->ar = ar;
@@ -2247,54 +2364,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_iomap;
        }
 
-       /*
-        * Bring the target up cleanly.
-        *
-        * The target may be in an undefined state with an AUX-powered Target
-        * and a Host in WoW mode. If the Host crashes, loses power, or is
-        * restarted (without unloading the driver) then the Target is left
-        * (aux) powered and running. On a subsequent driver load, the Target
-        * is in an unexpected state. We try to catch that here in order to
-        * reset the Target and retry the probe.
-        */
-       ath10k_pci_device_reset(ar_pci);
-
-       ret = ath10k_pci_reset_target(ar);
-       if (ret)
-               goto err_intr;
-
-       if (ath10k_target_ps) {
-               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-       } else {
-               /* Force AWAKE forever */
-               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-               ath10k_do_pci_wake(ar);
-       }
-
-       ret = ath10k_pci_ce_init(ar);
-       if (ret)
-               goto err_intr;
-
-       ret = ath10k_pci_init_config(ar);
-       if (ret)
-               goto err_ce;
-
-       ret = ath10k_pci_wake_target_cpu(ar);
-       if (ret) {
-               ath10k_err("could not wake up target CPU (%d)\n", ret);
-               goto err_ce;
-       }
-
        ret = ath10k_core_register(ar);
        if (ret) {
                ath10k_err("could not register driver core (%d)\n", ret);
-               goto err_ce;
+               goto err_intr;
        }
 
        return 0;
 
-err_ce:
-       ath10k_pci_ce_deinit(ar);
 err_intr:
        ath10k_pci_stop_intr(ar);
 err_iomap:
@@ -2345,128 +2422,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        kfree(ar_pci);
 }
 
-#if defined(CONFIG_PM_SLEEP)
-
-#define ATH10K_PCI_PM_CONTROL 0x44
-
-static int ath10k_pci_suspend(struct device *device)
-{
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct ath10k *ar = pci_get_drvdata(pdev);
-       struct ath10k_pci *ar_pci;
-       u32 val;
-       int ret, retval;
-
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-       if (!ar)
-               return -ENODEV;
-
-       ar_pci = ath10k_pci_priv(ar);
-       if (!ar_pci)
-               return -ENODEV;
-
-       if (ath10k_core_target_suspend(ar))
-               return -EBUSY;
-
-       ret = wait_event_interruptible_timeout(ar->event_queue,
-                                               ar->is_target_paused == true,
-                                               1 * HZ);
-       if (ret < 0) {
-               ath10k_warn("suspend interrupted (%d)\n", ret);
-               retval = ret;
-               goto resume;
-       } else if (ret == 0) {
-               ath10k_warn("suspend timed out - target pause event never came\n");
-               retval = EIO;
-               goto resume;
-       }
-
-       /*
-        * reset is_target_paused and host can check that in next time,
-        * or it will always be TRUE and host just skip the waiting
-        * condition, it causes target assert due to host already
-        * suspend
-        */
-       ar->is_target_paused = false;
-
-       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-       if ((val & 0x000000ff) != 0x3) {
-               pci_save_state(pdev);
-               pci_disable_device(pdev);
-               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-                                      (val & 0xffffff00) | 0x03);
-       }
-
-       return 0;
-resume:
-       ret = ath10k_core_target_resume(ar);
-       if (ret)
-               ath10k_warn("could not resume (%d)\n", ret);
-
-       return retval;
-}
-
-static int ath10k_pci_resume(struct device *device)
-{
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct ath10k *ar = pci_get_drvdata(pdev);
-       struct ath10k_pci *ar_pci;
-       int ret;
-       u32 val;
-
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-       if (!ar)
-               return -ENODEV;
-       ar_pci = ath10k_pci_priv(ar);
-
-       if (!ar_pci)
-               return -ENODEV;
-
-       ret = pci_enable_device(pdev);
-       if (ret) {
-               ath10k_warn("cannot enable PCI device: %d\n", ret);
-               return ret;
-       }
-
-       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-       if ((val & 0x000000ff) != 0) {
-               pci_restore_state(pdev);
-               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-                                      val & 0xffffff00);
-               /*
-                * 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_read_config_dword(pdev, 0x40, &val);
-
-               if ((val & 0x0000ff00) != 0)
-                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-       }
-
-       ret = ath10k_core_target_resume(ar);
-       if (ret)
-               ath10k_warn("target resume failed: %d\n", ret);
-
-       return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
-                        ath10k_pci_suspend,
-                        ath10k_pci_resume);
-
-#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
-
-#else
-
-#define ATH10K_PCI_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
 MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
 
 static struct pci_driver ath10k_pci_driver = {
@@ -2474,7 +2429,6 @@ static struct pci_driver ath10k_pci_driver = {
        .id_table = ath10k_pci_id_table,
        .probe = ath10k_pci_probe,
        .remove = ath10k_pci_remove,
-       .driver.pm = ATH10K_PCI_PM_OPS,
 };
 
 static int __init ath10k_pci_init(void)
index d2a055a..d3a2e6c 100644 (file)
@@ -152,7 +152,7 @@ struct service_to_pipe {
 
 enum ath10k_pci_features {
        ATH10K_PCI_FEATURE_MSI_X                = 0,
-       ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND    = 1,
+       ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND    = 1,
 
        /* keep last */
        ATH10K_PCI_FEATURE_COUNT
@@ -311,7 +311,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *addr = ar_pci->mem;
 
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
                unsigned long irq_flags;
 
                spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
index 7d4b798..5e42460 100644 (file)
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->state == ATH10K_STATE_WEDGED) {
+               ath10k_warn("wmi flush skipped - device is wedged anyway\n");
+               return;
+       }
+
        ret = wait_event_timeout(ar->wmi.wq,
                                 atomic_read(&ar->wmi.pending_tx_count) == 0,
                                 5*HZ);
@@ -111,7 +118,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
 
        trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
 
-       status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+       status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
        if (status) {
                dev_kfree_skb_any(skb);
                atomic_dec(&ar->wmi.pending_tx_count);
@@ -501,8 +508,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
        ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
                                    (u8 *)skb_tail_pointer(bcn) - ies);
        if (!ie) {
-               /* highly unlikely for mac80211 */
-               ath10k_warn("no tim ie found;\n");
+               if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+                       ath10k_warn("no tim ie found;\n");
                return;
        }
 
@@ -1114,7 +1121,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
        /* connect to control service */
        conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
 
-       status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+       status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
        if (status) {
                ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
                            status);
@@ -1748,6 +1755,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
        if (arg->key_data)
                memcpy(cmd->key_data, arg->key_data, arg->key_len);
 
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev install key idx %d cipher %d len %d\n",
+                  arg->key_idx, arg->key_cipher, arg->key_len);
        return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
 }
 
@@ -2011,6 +2021,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
        cmd->peer_vht_rates.tx_mcs_set =
                __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
 
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer assoc vdev %d addr %pM\n",
+                  arg->vdev_id, arg->addr);
        return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
 }
 
@@ -2079,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
        ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
        return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
 }
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+                            enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+       struct wmi_force_fw_hang_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+       cmd->type = __cpu_to_le32(type);
+       cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+                  type, delay_ms);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
index 9555f5a..da3b2bc 100644 (file)
@@ -416,6 +416,7 @@ enum wmi_cmd_id {
        WMI_PDEV_FTM_INTG_CMDID,
        WMI_VDEV_SET_KEEPALIVE_CMDID,
        WMI_VDEV_GET_KEEPALIVE_CMDID,
+       WMI_FORCE_FW_HANG_CMDID,
 
        /* GPIO Configuration */
        WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
        struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+       WMI_FORCE_FW_HANG_ASSERT = 1,
+       WMI_FORCE_FW_HANG_NO_DETECT,
+       WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+       WMI_FORCE_FW_HANG_EMPTY_POINT,
+       WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+       WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+       __le32 type;
+       __le32 delay_ms;
+} __packed;
+
 #define ATH10K_RTS_MAX         2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN    540
 #define ATH10K_FRAGMT_THRESHOLD_MAX    2346
@@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
                        const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+                            enum wmi_force_fw_hang_type type, u32 delay_ms);
 
 #endif /* _WMI_H_ */
index d491a31..c91bc61 100644 (file)
@@ -96,6 +96,16 @@ config ATH9K_LEGACY_RATE_CONTROL
          has to be passed to mac80211 using the module parameter,
          ieee80211_default_rc_algo.
 
+config ATH9K_RFKILL
+       bool "Atheros ath9k rfkill support" if EXPERT
+       depends on ATH9K
+       depends on RFKILL=y || RFKILL=ATH9K
+       default y
+       help
+         Say Y to have ath9k poll the RF-Kill GPIO every couple of
+         seconds. Turn off to save power, but enable it if you have
+         a platform that can toggle the RF-Kill GPIO.
+
 config ATH9K_HTC
        tristate "Atheros HTC based wireless cards support"
        depends on USB && MAC80211
index 664844c..dd1cc73 100644 (file)
 
 #include "ath9k.h"
 
-static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+/*
+ * AR9285
+ * ======
+ *
+ * EEPROM has 2 4-bit fields containing the card configuration.
+ *
+ * antdiv_ctl1:
+ * ------------
+ * bb_enable_ant_div_lnadiv : 1
+ * bb_ant_div_alt_gaintb    : 1
+ * bb_ant_div_main_gaintb   : 1
+ * bb_enable_ant_fast_div   : 1
+ *
+ * antdiv_ctl2:
+ * -----------
+ * bb_ant_div_alt_lnaconf  : 2
+ * bb_ant_div_main_lnaconf : 2
+ *
+ * The EEPROM bits are used as follows:
+ * ------------------------------------
+ *
+ * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
+ *                                 1 -> Antenna config Alt/Main uses gaintable 1
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_enable_ant_fast_div        - Enable fast antenna diversity.
+ *                                 Set in AR_PHY_CCK_DETECT.
+ *
+ * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *                                 10=LNA1
+ *                                 01=LNA2
+ *                                 11=LNA1+LNA2
+ *                                 00=LNA1-LNA2
+ *
+ * AR9485 / AR9565 / AR9331
+ * ========================
+ *
+ * The same bits are present in the EEPROM, but the location in the
+ * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
+ *
+ * ant_div_alt_lnaconf      ==> bit 0~1
+ * ant_div_main_lnaconf     ==> bit 2~3
+ * ant_div_alt_gaintb       ==> bit 4
+ * ant_div_main_gaintb      ==> bit 5
+ * enable_ant_div_lnadiv    ==> bit 6
+ * enable_ant_fast_div      ==> bit 7
+ */
+
+static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
+                                              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);
+       if (pkt_count <= 50)
+               return false;
+
+       if (alt_rssi_avg > main_rssi_avg + mindelta)
+               return true;
+
+       if (alt_ratio >= antcomb->ant_ratio2 &&
+           alt_rssi_avg >= antcomb->low_rssi_thresh &&
+           (alt_rssi_avg > main_rssi_avg + maxdelta))
+               return true;
+
+       return false;
 }
 
-static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
-                                             int curr_main_set, int curr_alt_set,
-                                             int alt_rssi_avg, int main_rssi_avg)
+static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
+                                             struct ath_ant_comb *antcomb,
+                                             int alt_ratio, int alt_rssi_avg,
+                                             int main_rssi_avg)
 {
-       bool result = false;
-       switch (div_group) {
+       bool result, set1, set2;
+
+       result = set1 = set2 = false;
+
+       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
+           conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+               set1 = true;
+
+       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
+           conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+               set2 = true;
+
+       switch (conf->div_group) {
        case 0:
                if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
                        result = true;
                break;
        case 1:
        case 2:
-               if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
-                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
-                     (alt_rssi_avg >= (main_rssi_avg - 5))) ||
-                    ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
-                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
-                     (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
-                   (alt_rssi_avg >= 4))
+               if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+                       break;
+
+               if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+                   (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
+                   (alt_ratio > antcomb->ant_ratio))
                        result = true;
-               else
-                       result = false;
+
+               break;
+       case 3:
+               if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+                       break;
+
+               if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
+                   (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
+                   (alt_ratio > antcomb->ant_ratio))
+                       result = true;
+
                break;
        }
 
@@ -108,6 +190,74 @@ static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
        }
 }
 
+static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
+                                 struct ath_hw_antcomb_conf *conf)
+{
+       /* 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 (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                                       conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               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 (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               } else {
+                       /* Set alt to A+B or A-B */
+                       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 (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       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 (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       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 (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       conf->alt_lna_conf = antcomb->main_conf;
+       }
+}
+
 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,
@@ -129,7 +279,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
 
                if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
                        /* main is LNA1 */
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -138,7 +288,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->first_ratio = false;
                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -147,11 +297,11 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        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))
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               0,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
                                antcomb->first_ratio = true;
                        else
                                antcomb->first_ratio = false;
@@ -164,17 +314,21 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                antcomb->rssi_first = main_rssi_avg;
                antcomb->rssi_third = alt_rssi_avg;
 
-               if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+               switch(antcomb->second_quick_scan_conf) {
+               case ATH_ANT_DIV_COMB_LNA1:
                        antcomb->rssi_lna1 = alt_rssi_avg;
-               else if (antcomb->second_quick_scan_conf ==
-                        ATH_ANT_DIV_COMB_LNA2)
+                       break;
+               case 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) {
+                       break;
+               case 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;
+                       break;
+               default:
+                       break;
                }
 
                if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
@@ -184,7 +338,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        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,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -193,7 +347,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->second_ratio = false;
                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -202,105 +356,18 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        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))
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               0,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
                                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;
-               }
+               ath_ant_set_alt_ratio(antcomb, div_ant_conf);
+
                break;
        default:
                break;
@@ -430,8 +497,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x10: /* LNA2 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -440,15 +506,13 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x13: /* LNA2 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
                        break;
                case 0x20: /* LNA1 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -457,8 +521,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x23: /* LNA1 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -475,6 +538,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                default:
                        break;
                }
+
+               if (antcomb->fast_div_bias)
+                       ant_conf->fast_div_bias = antcomb->fast_div_bias;
        } else if (ant_conf->div_group == 3) {
                switch ((ant_conf->main_lna_conf << 4) |
                        ant_conf->alt_lna_conf) {
@@ -540,6 +606,138 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
        }
 }
 
+static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
+                            struct ath_hw_antcomb_conf *conf,
+                            int curr_alt_set, int alt_rssi_avg,
+                            int main_rssi_avg)
+{
+       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 */
+               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+               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 */
+               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               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 */
+               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 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       } else if (antcomb->rssi_sub >
+                                  antcomb->rssi_lna1) {
+                               /* set to A-B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       } else {
+                               /* set to LNA1 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               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 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       } else if (antcomb->rssi_sub >
+                                  antcomb->rssi_lna1) {
+                               /* set to A-B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       } else {
+                               /* set to LNA2 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
+                              struct ath_ant_comb *antcomb,
+                              int alt_ratio, int alt_rssi_avg,
+                              int main_rssi_avg, int curr_main_set,
+                              int curr_alt_set)
+{
+       bool ret = false;
+
+       if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
+                                      alt_rssi_avg, main_rssi_avg)) {
+               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;
+               }
+
+               ret = true;
+       } 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;
+
+               ret = true;
+       }
+
+       return ret;
+}
+
+static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
+{
+       int alt_ratio;
+
+       if (!antcomb->scan || !antcomb->alt_good)
+               return false;
+
+       if (time_after(jiffies, antcomb->scan_start_time +
+                      msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+               return true;
+
+       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 < antcomb->ant_ratio)
+                       return true;
+       }
+
+       return false;
+}
+
 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
 {
        struct ath_hw_antcomb_conf div_ant_conf;
@@ -549,41 +747,46 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
        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;
+       bool short_scan = false, ret;
 
        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;
 
+       if (alt_rssi >= antcomb->low_rssi_thresh) {
+               antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
+               antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
+       } else {
+               antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
+               antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
+       }
+
        /* Record packet only when both main_rssi and  alt_rssi is positive */
        if (main_rssi > 0 && 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 (main_ant_conf == rx_ant_conf) {
+               ANT_STAT_INC(ANT_MAIN, recv_cnt);
+               ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
+       } else {
+               ANT_STAT_INC(ANT_ALT, recv_cnt);
+               ANT_LNA_INC(ANT_ALT, rx_ant_conf);
        }
 
+       /* Short scan check */
+       short_scan = ath_ant_short_scan_check(antcomb);
+
        if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
-           rs->rs_moreaggr) && !short_scan)
+            rs->rs_moreaggr) && !short_scan)
                return;
 
        if (antcomb->total_pkt_count) {
@@ -595,15 +798,13 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
                                 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;
-
        antcomb->count++;
 
        if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
-               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+               if (alt_ratio > antcomb->ant_ratio) {
                        ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
                                                  main_rssi_avg);
                        antcomb->alt_good = true;
@@ -617,153 +818,47 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
        }
 
        if (!antcomb->scan) {
-               if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
-                                       alt_ratio, curr_main_set, curr_alt_set,
-                                       alt_rssi_avg, main_rssi_avg)) {
-                       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 +
-                                    div_ant_conf.lna1_lna2_delta)))
+               ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
+                                        alt_rssi_avg, main_rssi_avg,
+                                        curr_main_set, curr_alt_set);
+               if (ret)
                        goto div_comb_done;
        }
 
+       if (!antcomb->scan &&
+           (alt_rssi_avg < (main_rssi_avg + div_ant_conf.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;
-               }
+               ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
+                                alt_rssi_avg, main_rssi_avg);
        } 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;
+                                       ATH_ANT_DIV_COMB_LNA2;
                                div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
+                                       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;
+                                       ATH_ANT_DIV_COMB_LNA1;
                                div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
+                                       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++;
        }
 
-       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, antcomb, alt_ratio);
        ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+       ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
 
        antcomb->scan_start_time = jiffies;
        antcomb->total_pkt_count = 0;
@@ -772,26 +867,3 @@ div_comb_done:
        antcomb->main_recv_cnt = 0;
        antcomb->alt_recv_cnt = 0;
 }
-
-void ath_ant_comb_update(struct ath_softc *sc)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_hw_antcomb_conf div_ant_conf;
-       u8 lna_conf;
-
-       ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
-
-       if (sc->ant_rx == 1)
-               lna_conf = ATH_ANT_DIV_COMB_LNA1;
-       else
-               lna_conf = ATH_ANT_DIV_COMB_LNA2;
-
-       div_ant_conf.main_lna_conf = lna_conf;
-       div_ant_conf.alt_lna_conf = lna_conf;
-
-       ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
-
-       if (common->antenna_diversity)
-               ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
-}
index f400351..456d8b9 100644 (file)
@@ -555,6 +555,65 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
 }
 
+static void ar9002_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+{
+       struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
+       u8 antdiv_ctrl1, antdiv_ctrl2;
+       u32 regval;
+
+       if (enable) {
+               antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE;
+               antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE;
+
+               /*
+                * Don't disable BT ant to allow BB to control SWCOM.
+                */
+               btcoex->bt_coex_mode2 &= (~(AR_BT_DISABLE_BT_ANT));
+               REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+               REG_WRITE(ah, AR_PHY_SWITCH_COM, ATH_BT_COEX_ANT_DIV_SWITCH_COM);
+               REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+       } else {
+               /*
+                * Disable antenna diversity, use LNA1 only.
+                */
+               antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A;
+               antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A;
+
+               /*
+                * Disable BT Ant. to allow concurrent BT and WLAN receive.
+                */
+               btcoex->bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT;
+               REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+               /*
+                * Program SWCOM table to make sure RF switch always parks
+                * at BT side.
+                */
+               REG_WRITE(ah, AR_PHY_SWITCH_COM, 0);
+               REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+       }
+
+       regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+       regval &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
+        /*
+        * Clear ant_fast_div_bias [14:9] since for WB195,
+        * the main LNA is always LNA1.
+        */
+       regval &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+       regval |= SM(antdiv_ctrl1, AR_PHY_9285_ANT_DIV_CTL);
+       regval |= SM(antdiv_ctrl2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+       regval |= SM((antdiv_ctrl2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+       regval |= SM((antdiv_ctrl1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
+       regval |= SM((antdiv_ctrl1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
+       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+
+       regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+       regval &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+       regval |= SM((antdiv_ctrl1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+       REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+}
+
 static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
                                    struct ath_spec_scan *param)
 {
@@ -630,6 +689,7 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
 
        ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
+       ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
        ops->spectral_scan_config = ar9002_hw_spectral_scan_config;
        ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
        ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;
index f9eb2c3..6314ae2 100644 (file)
 #define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S    29
 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB     0x40000000
 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S   30
-#define AR_PHY_9285_ANT_DIV_LNA1            2
-#define AR_PHY_9285_ANT_DIV_LNA2            1
-#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2  3
-#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0
 #define AR_PHY_9285_ANT_DIV_GAINTB_0        0
 #define AR_PHY_9285_ANT_DIV_GAINTB_1        1
 
+#define ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE  0x0b
+#define ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE  0x09
+#define ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04
+#define ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09
+#define ATH_BT_COEX_ANT_DIV_SWITCH_COM      0x66666666
+
 #define AR_PHY_EXT_CCA0             0x99b8
 #define AR_PHY_EXT_CCA0_THRESH62    0x000000FF
 #define AR_PHY_EXT_CCA0_THRESH62_S  0
index d105e43..abdc7ee 100644 (file)
@@ -3541,13 +3541,12 @@ static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is2ghz)
        return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt);
 }
 
-
-static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
 {
        return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon);
 }
 
-static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
 {
        return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2);
 }
@@ -3561,6 +3560,7 @@ static u16 ar9003_hw_ant_ctrl_chain_get(struct ath_hw *ah, int chain,
 
 static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        int chain;
        u32 regval, value, gpio;
@@ -3614,6 +3614,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
        }
 
        value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
+       if (AR_SREV_9485(ah) && common->bt_ant_diversity) {
+               regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+               regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+
+       }
        REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
 
        if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
@@ -3645,8 +3650,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                regval &= (~AR_PHY_ANT_DIV_LNADIV);
                regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
 
+               if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+                       regval |= AR_ANT_DIV_ENABLE;
+
                if (AR_SREV_9565(ah)) {
-                       if (ah->shared_chain_lnadiv) {
+                       if (common->bt_ant_diversity) {
                                regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
                        } else {
                                regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
@@ -3656,10 +3664,14 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 
                REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 
-               /*enable fast_div */
+               /* enable fast_div */
                regval = REG_READ(ah, AR_PHY_CCK_DETECT);
                regval &= (~AR_FAST_DIV_ENABLE);
                regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+
+               if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+                       regval |= AR_FAST_DIV_ENABLE;
+
                REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
 
                if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
@@ -3673,9 +3685,9 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                                     AR_PHY_ANT_DIV_ALT_GAINTB |
                                     AR_PHY_ANT_DIV_MAIN_GAINTB));
                        /* by default use LNA1 for the main antenna */
-                       regval |= (AR_PHY_ANT_DIV_LNA1 <<
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
                                   AR_PHY_ANT_DIV_MAIN_LNACONF_S);
-                       regval |= (AR_PHY_ANT_DIV_LNA2 <<
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
                                   AR_PHY_ANT_DIV_ALT_LNACONF_S);
                        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
                }
index 874f657..75d4fb4 100644 (file)
@@ -334,6 +334,8 @@ struct ar9300_eeprom {
 
 s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah);
 s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah);
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz);
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz);
 
 u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
 
index 1f694ab..4898829 100644 (file)
@@ -632,6 +632,22 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
 
        REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
                    AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+               REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
+                         AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
+
+               if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+                                  AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
+                       ah->enabled_cals |= TX_IQ_CAL;
+               else
+                       ah->enabled_cals &= ~TX_IQ_CAL;
+
+               if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE)
+                       ah->enabled_cals |= TX_CL_CAL;
+               else
+                       ah->enabled_cals &= ~TX_CL_CAL;
+       }
 }
 
 static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@ -814,29 +830,12 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
-               REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
-                         AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
-
        ah->modes_index = modesIndex;
        ar9003_hw_override_ini(ah);
        ar9003_hw_set_channel_regs(ah, chan);
        ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
        ath9k_hw_apply_txpower(ah, chan, false);
 
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-               if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
-                                  AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
-                       ah->enabled_cals |= TX_IQ_CAL;
-               else
-                       ah->enabled_cals &= ~TX_IQ_CAL;
-
-               if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE)
-                       ah->enabled_cals |= TX_CL_CAL;
-               else
-                       ah->enabled_cals &= ~TX_CL_CAL;
-       }
-
        return 0;
 }
 
@@ -1413,62 +1412,104 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 }
 
-static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
-                                                 bool enable)
+static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
 {
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        u8 ant_div_ctl1;
        u32 regval;
 
-       if (!AR_SREV_9565(ah))
+       if (!AR_SREV_9485(ah) && !AR_SREV_9565(ah))
                return;
 
-       ah->shared_chain_lnadiv = enable;
+       if (AR_SREV_9485(ah)) {
+               regval = ar9003_hw_ant_ctrl_common_2_get(ah,
+                                                IS_CHAN_2GHZ(ah->curchan));
+               if (enable) {
+                       regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+                       regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+               }
+               REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2,
+                             AR_SWITCH_TABLE_COM2_ALL, regval);
+       }
+
        ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
 
+       /*
+        * Set MAIN/ALT LNA conf.
+        * Set MAIN/ALT gain_tb.
+        */
        regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
        regval &= (~AR_ANT_DIV_CTRL_ALL);
        regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S;
-       regval &= ~AR_PHY_ANT_DIV_LNADIV;
-       regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
-
-       if (enable)
-               regval |= AR_ANT_DIV_ENABLE;
-
        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 
-       regval = REG_READ(ah, AR_PHY_CCK_DETECT);
-       regval &= ~AR_FAST_DIV_ENABLE;
-       regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
-
-       if (enable)
-               regval |= AR_FAST_DIV_ENABLE;
-
-       REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
-
-       if (enable) {
-               REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
-                           (1 << AR_PHY_ANT_SW_RX_PROT_S));
-               if (ah->curchan && IS_CHAN_2GHZ(ah->curchan))
-                       REG_SET_BIT(ah, AR_PHY_RESTART,
-                                   AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
-               REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
-                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
-       } else {
-               REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
-               REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
-                           (1 << AR_PHY_ANT_SW_RX_PROT_S));
-               REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
-               REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
-                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
-
+       if (AR_SREV_9485_11(ah)) {
+               /*
+                * Enable LNA diversity.
+                */
                regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
-               regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
-                       AR_PHY_ANT_DIV_ALT_LNACONF |
-                       AR_PHY_ANT_DIV_MAIN_GAINTB |
-                       AR_PHY_ANT_DIV_ALT_GAINTB);
-               regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S);
-               regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S);
+               regval &= ~AR_PHY_ANT_DIV_LNADIV;
+               regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+               if (enable)
+                       regval |= AR_ANT_DIV_ENABLE;
+
                REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+
+               /*
+                * Enable fast antenna diversity.
+                */
+               regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+               regval &= ~AR_FAST_DIV_ENABLE;
+               regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+               if (enable)
+                       regval |= AR_FAST_DIV_ENABLE;
+
+               REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+
+               if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+                       regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+                       regval &= (~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+                                    AR_PHY_ANT_DIV_ALT_LNACONF |
+                                    AR_PHY_ANT_DIV_ALT_GAINTB |
+                                    AR_PHY_ANT_DIV_MAIN_GAINTB));
+                       /*
+                        * Set MAIN to LNA1 and ALT to LNA2 at the
+                        * beginning.
+                        */
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_ANT_DIV_ALT_LNACONF_S);
+                       REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+               }
+       } else if (AR_SREV_9565(ah)) {
+               if (enable) {
+                       REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   (1 << AR_PHY_ANT_SW_RX_PROT_S));
+                       if (ah->curchan && IS_CHAN_2GHZ(ah->curchan))
+                               REG_SET_BIT(ah, AR_PHY_RESTART,
+                                           AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
+                       REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                   AR_BTCOEX_WL_LNADIV_FORCE_ON);
+               } else {
+                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   (1 << AR_PHY_ANT_SW_RX_PROT_S));
+                       REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                   AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
+                       regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+                       regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+                                   AR_PHY_ANT_DIV_ALT_LNACONF |
+                                   AR_PHY_ANT_DIV_MAIN_GAINTB |
+                                   AR_PHY_ANT_DIV_ALT_GAINTB);
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_ANT_DIV_ALT_LNACONF_S);
+                       REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+               }
        }
 }
 
@@ -1518,6 +1559,18 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
 
        REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
 
+       if (AR_SREV_9462_20_OR_LATER(ah)) {
+               /*
+                * CUS217 mix LNA mode.
+                */
+               if (ar9003_hw_get_rx_gain_idx(ah) == 2) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                                       1, regWrites);
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                                       modesIndex, regWrites);
+               }
+       }
+
        /*
         * For 5GHz channels requiring Fast Clock, apply
         * different modal values.
@@ -1528,7 +1581,11 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
        if (AR_SREV_9565(ah))
                REG_WRITE_ARRAY(&ah->iniModesFastClock, 1, regWrites);
 
-       REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
+       /*
+        * JAPAN regulatory.
+        */
+       if (chan->channel == 2484)
+               ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
        ah->modes_index = modesIndex;
        *ini_reloaded = true;
@@ -1631,7 +1688,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 
        ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
-       ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
+       ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity;
        ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
        ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger;
        ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait;
index d4d39f3..23c019d 100644 (file)
 #define AR_PHY_ANT_DIV_MAIN_GAINTB              0x40000000
 #define AR_PHY_ANT_DIV_MAIN_GAINTB_S            30
 
-#define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2          0x0
-#define AR_PHY_ANT_DIV_LNA2                     0x1
-#define AR_PHY_ANT_DIV_LNA1                     0x2
-#define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2           0x3
-
 #define AR_PHY_EXTCHN_PWRTHR1   (AR_AGC_BASE + 0x2c)
 #define AR_PHY_EXT_CHN_WIN      (AR_AGC_BASE + 0x30)
 #define AR_PHY_20_40_DET_THR    (AR_AGC_BASE + 0x34)
index c1224b5..c497d52 100644 (file)
@@ -585,19 +585,14 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
 #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_ALT_ANT_RATIO_LOW_RSSI 50
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50
 
 #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;
@@ -614,27 +609,35 @@ struct ath_ant_comb {
        int rssi_first;
        int rssi_second;
        int rssi_third;
+       int ant_ratio;
+       int ant_ratio2;
        bool alt_good;
        int quick_scan_cnt;
-       int main_conf;
+       enum ath9k_ant_div_comb_lna_conf main_conf;
        enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
        enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
        bool first_ratio;
        bool second_ratio;
        unsigned long scan_start_time;
+
+       /*
+        * Card-specific config values.
+        */
+       int low_rssi_thresh;
+       int fast_div_bias;
 };
 
 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
-void ath_ant_comb_update(struct ath_softc *sc);
 
 /********************/
 /* Main driver core */
 /********************/
 
-#define ATH9K_PCI_CUS198 0x0001
-#define ATH9K_PCI_CUS230 0x0002
-#define ATH9K_PCI_CUS217 0x0004
-#define ATH9K_PCI_WOW    0x0008
+#define ATH9K_PCI_CUS198     0x0001
+#define ATH9K_PCI_CUS230     0x0002
+#define ATH9K_PCI_CUS217     0x0004
+#define ATH9K_PCI_WOW        0x0008
+#define ATH9K_PCI_BT_ANT_DIV 0x0010
 
 /*
  * Default cache line size, in bytes.
index 87454f6..a155190 100644 (file)
@@ -270,25 +270,27 @@ static const struct file_operations fops_ani = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_ant_diversity(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
+static ssize_t read_file_bt_ant_diversity(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        char buf[32];
        unsigned int len;
 
-       len = sprintf(buf, "%d\n", common->antenna_diversity);
+       len = sprintf(buf, "%d\n", common->bt_ant_diversity);
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
-static ssize_t write_file_ant_diversity(struct file *file,
-                                       const char __user *user_buf,
-                                       size_t count, loff_t *ppos)
+static ssize_t write_file_bt_ant_diversity(struct file *file,
+                                          const char __user *user_buf,
+                                          size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       unsigned long antenna_diversity;
+       struct ath9k_hw_capabilities *pCap = &sc->sc_ah->caps;
+       unsigned long bt_ant_diversity;
        char buf[32];
        ssize_t len;
 
@@ -296,26 +298,145 @@ static ssize_t write_file_ant_diversity(struct file *file,
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
 
-       if (!AR_SREV_9565(sc->sc_ah))
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
                goto exit;
 
        buf[len] = '\0';
-       if (kstrtoul(buf, 0, &antenna_diversity))
+       if (kstrtoul(buf, 0, &bt_ant_diversity))
                return -EINVAL;
 
-       common->antenna_diversity = !!antenna_diversity;
+       common->bt_ant_diversity = !!bt_ant_diversity;
        ath9k_ps_wakeup(sc);
-       ath_ant_comb_update(sc);
-       ath_dbg(common, CONFIG, "Antenna diversity: %d\n",
-               common->antenna_diversity);
+       ath9k_hw_set_bt_ant_diversity(sc->sc_ah, common->bt_ant_diversity);
+       ath_dbg(common, CONFIG, "Enable WLAN/BT RX Antenna diversity: %d\n",
+               common->bt_ant_diversity);
        ath9k_ps_restore(sc);
 exit:
        return count;
 }
 
-static const struct file_operations fops_ant_diversity = {
-       .read = read_file_ant_diversity,
-       .write = write_file_ant_diversity,
+static const struct file_operations fops_bt_ant_diversity = {
+       .read = read_file_bt_ant_diversity,
+       .write = write_file_bt_ant_diversity,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_debug_stat_ant(struct ath_softc *sc,
+                         struct ath_hw_antcomb_conf *div_ant_conf,
+                         int main_rssi_avg, int alt_rssi_avg)
+{
+       struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN];
+       struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT];
+
+       as_main->lna_attempt_cnt[div_ant_conf->main_lna_conf]++;
+       as_alt->lna_attempt_cnt[div_ant_conf->alt_lna_conf]++;
+
+       as_main->rssi_avg = main_rssi_avg;
+       as_alt->rssi_avg = alt_rssi_avg;
+}
+
+static ssize_t read_file_antenna_diversity(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
+       struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN];
+       struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT];
+       struct ath_hw_antcomb_conf div_ant_conf;
+       unsigned int len = 0, size = 1024;
+       ssize_t retval = 0;
+       char *buf;
+       char *lna_conf_str[4] = {"LNA1_MINUS_LNA2",
+                                "LNA2",
+                                "LNA1",
+                                "LNA1_PLUS_LNA2"};
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) {
+               len += snprintf(buf + len, size - len, "%s\n",
+                               "Antenna Diversity Combining is disabled");
+               goto exit;
+       }
+
+       ath9k_ps_wakeup(sc);
+       ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+       len += snprintf(buf + len, size - len, "Current MAIN config : %s\n",
+                       lna_conf_str[div_ant_conf.main_lna_conf]);
+       len += snprintf(buf + len, size - len, "Current ALT config  : %s\n",
+                       lna_conf_str[div_ant_conf.alt_lna_conf]);
+       len += snprintf(buf + len, size - len, "Average MAIN RSSI   : %d\n",
+                       as_main->rssi_avg);
+       len += snprintf(buf + len, size - len, "Average ALT RSSI    : %d\n\n",
+                       as_alt->rssi_avg);
+       ath9k_ps_restore(sc);
+
+       len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n");
+       len += snprintf(buf + len, size - len, "-------------------\n");
+
+       len += snprintf(buf + len, size - len, "%30s%15s\n",
+                       "MAIN", "ALT");
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "TOTAL COUNT",
+                       as_main->recv_cnt,
+                       as_alt->recv_cnt);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 + LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 - LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+
+       len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n");
+       len += snprintf(buf + len, size - len, "--------------------\n");
+
+       len += snprintf(buf + len, size - len, "%30s%15s\n",
+                       "MAIN", "ALT");
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 + LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 - LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+
+exit:
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_antenna_diversity = {
+       .read = read_file_antenna_diversity,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -1814,8 +1935,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
        debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
-       debugfs_create_file("diversity", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_ant_diversity);
+       debugfs_create_file("bt_ant_diversity", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_bt_ant_diversity);
+       debugfs_create_file("antenna_diversity", S_IRUSR,
+                           sc->debug.debugfs_phy, sc, &fops_antenna_diversity);
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
        debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_btcoex);
index fc67919..01c5c6a 100644 (file)
@@ -28,9 +28,13 @@ struct fft_sample_tlv;
 #ifdef CONFIG_ATH9K_DEBUGFS
 #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
 #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++
+#define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++
+#define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++;
 #else
 #define TX_STAT_INC(q, c) do { } while (0)
 #define RESET_STAT_INC(sc, type) do { } while (0)
+#define ANT_STAT_INC(i, c) do { } while (0)
+#define ANT_LNA_INC(i, c) do { } while (0)
 #endif
 
 enum ath_reset_type {
@@ -243,11 +247,22 @@ struct ath_rx_stats {
        u32 rx_spectral;
 };
 
+#define ANT_MAIN 0
+#define ANT_ALT  1
+
+struct ath_antenna_stats {
+       u32 recv_cnt;
+       u32 rssi_avg;
+       u32 lna_recv_cnt[4];
+       u32 lna_attempt_cnt[4];
+};
+
 struct ath_stats {
        struct ath_interrupt_stats istats;
        struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
        struct ath_rx_stats rxstats;
        struct ath_dfs_stats dfs_stats;
+       struct ath_antenna_stats ant_stats[2];
        u32 reset[__RESET_TYPE_MAX];
 };
 
@@ -281,10 +296,11 @@ void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              struct ieee80211_sta *sta,
                              struct dentry *dir);
-
 void ath_debug_send_fft_sample(struct ath_softc *sc,
                               struct fft_sample_tlv *fft_sample);
-
+void ath9k_debug_stat_ant(struct ath_softc *sc,
+                         struct ath_hw_antcomb_conf *div_ant_conf,
+                         int main_rssi_avg, int alt_rssi_avg);
 #else
 
 #define RX_STAT_INC(c) /* NOP */
@@ -297,12 +313,10 @@ static inline int ath9k_init_debug(struct ath_hw *ah)
 static inline void ath9k_deinit_debug(struct ath_softc *sc)
 {
 }
-
 static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
                                            enum ath9k_int status)
 {
 }
-
 static inline void ath_debug_stat_tx(struct ath_softc *sc,
                                     struct ath_buf *bf,
                                     struct ath_tx_status *ts,
@@ -310,11 +324,16 @@ static inline void ath_debug_stat_tx(struct ath_softc *sc,
                                     unsigned int flags)
 {
 }
-
 static inline void ath_debug_stat_rx(struct ath_softc *sc,
                                     struct ath_rx_status *rs)
 {
 }
+static inline void ath9k_debug_stat_ant(struct ath_softc *sc,
+                                       struct ath_hw_antcomb_conf *div_ant_conf,
+                                       int main_rssi_avg, int alt_rssi_avg)
+{
+
+}
 
 #endif /* CONFIG_ATH9K_DEBUGFS */
 
index c2bfd74..9ea8e4b 100644 (file)
@@ -812,6 +812,7 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
 static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
                                         struct ath9k_channel *chan)
 {
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct modal_eep_4k_header *pModal;
        struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
        struct base_eep_header_4k *pBase = &eep->baseEepHeader;
@@ -858,6 +859,24 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
 
                REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
                regVal = REG_READ(ah, AR_PHY_CCK_DETECT);
+
+               if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+                       /*
+                        * If diversity combining is enabled,
+                        * set MAIN to LNA1 and ALT to LNA2 initially.
+                        */
+                       regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+                       regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
+                                    AR_PHY_9285_ANT_DIV_ALT_LNACONF));
+
+                       regVal |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
+                       regVal |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
+                       regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+                       regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
+                       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
+               }
        }
 
        if (pModal->version >= 2) {
index 14b7011..a78d48c 100644 (file)
@@ -78,11 +78,10 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
 }
 
-static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
-                                                       bool enable)
+static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
 {
-       if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv)
-               ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable);
+       if (ath9k_hw_ops(ah)->set_bt_ant_diversity)
+               ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
 }
 
 /* Private hardware call ops */
index 4ca0cb0..151443b 100644 (file)
@@ -1496,16 +1496,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
                                    struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
+       bool band_switch = false, mode_diff = false;
+       u8 ini_reloaded = 0;
        u32 qnum;
        int r;
-       bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
-       bool band_switch, mode_diff;
-       u8 ini_reloaded;
 
-       band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
-                     (ah->curchan->channelFlags & (CHANNEL_2GHZ |
-                                                   CHANNEL_5GHZ));
-       mode_diff = (chan->chanmode != ah->curchan->chanmode);
+       if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
+               u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
+               u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
+               band_switch = (cur != new);
+               mode_diff = (chan->chanmode != ah->curchan->chanmode);
+       }
 
        for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
                if (ath9k_hw_numtxpending(ah, qnum)) {
@@ -1520,11 +1522,12 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
                return false;
        }
 
-       if (edma && (band_switch || mode_diff)) {
+       if (band_switch || mode_diff) {
                ath9k_hw_mark_phy_inactive(ah);
                udelay(5);
 
-               ath9k_hw_init_pll(ah, NULL);
+               if (band_switch)
+                       ath9k_hw_init_pll(ah, chan);
 
                if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
                        ath_err(common, "Failed to do fast channel change\n");
@@ -1541,22 +1544,21 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        }
        ath9k_hw_set_clockrate(ah);
        ath9k_hw_apply_txpower(ah, chan, false);
-       ath9k_hw_rfbus_done(ah);
 
        if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
                ath9k_hw_set_delta_slope(ah, chan);
 
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
-       if (edma && (band_switch || mode_diff)) {
-               ah->ah_flags |= AH_FASTCC;
-               if (band_switch || ini_reloaded)
-                       ah->eep_ops->set_board_values(ah, chan);
+       if (band_switch || ini_reloaded)
+               ah->eep_ops->set_board_values(ah, chan);
 
-               ath9k_hw_init_bb(ah, chan);
+       ath9k_hw_init_bb(ah, chan);
+       ath9k_hw_rfbus_done(ah);
 
-               if (band_switch || ini_reloaded)
-                       ath9k_hw_init_cal(ah, chan);
+       if (band_switch || ini_reloaded) {
+               ah->ah_flags |= AH_FASTCC;
+               ath9k_hw_init_cal(ah, chan);
                ah->ah_flags &= ~AH_FASTCC;
        }
 
@@ -1778,16 +1780,11 @@ static void ath9k_hw_init_desc(struct ath_hw *ah)
 /*
  * Fast channel change:
  * (Change synthesizer based on channel freq without resetting chip)
- *
- * Don't do FCC when
- *   - Flag is not set
- *   - Chip is just coming out of full sleep
- *   - Channel to be set is same as current channel
- *   - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel)
  */
 static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        int ret;
 
        if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
@@ -1806,9 +1803,21 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
            (CHANNEL_HALF | CHANNEL_QUARTER))
                goto fail;
 
-       if ((chan->channelFlags & CHANNEL_ALL) !=
-           (ah->curchan->channelFlags & CHANNEL_ALL))
-               goto fail;
+       /*
+        * If cross-band fcc is not supoprted, bail out if
+        * either channelFlags or chanmode differ.
+        *
+        * chanmode will be different if the HT operating mode
+        * changes because of CSA.
+        */
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
+               if ((chan->channelFlags & CHANNEL_ALL) !=
+                   (ah->curchan->channelFlags & CHANNEL_ALL))
+                       goto fail;
+
+               if (chan->chanmode != ah->curchan->chanmode)
+                       goto fail;
+       }
 
        if (!ath9k_hw_check_alive(ah))
                goto fail;
@@ -2047,7 +2056,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_apply_gpio_override(ah);
 
-       if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv)
+       if (AR_SREV_9565(ah) && common->bt_ant_diversity)
                REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
 
        return 0;
@@ -2504,7 +2513,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        else
                pCap->rts_aggr_limit = (8 * 1024);
 
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+#ifdef CONFIG_ATH9K_RFKILL
        ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT);
        if (ah->rfsilent & EEP_RFSILENT_ENABLED) {
                ah->rfkill_gpio =
@@ -2550,34 +2559,28 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
                pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
 
-       if (AR_SREV_9285(ah))
+       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))
+                       if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) {
                                pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+                               ath_info(common, "Enable LNA combining\n");
+                       }
                }
+       }
+
        if (AR_SREV_9300_20_OR_LATER(ah)) {
                if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE))
                        pCap->hw_caps |= ATH9K_HW_CAP_APM;
        }
 
-
        if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
                ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
-               /*
-                * enable the diversity-combining algorithm only when
-                * both enable_lna_div and enable_fast_div are set
-                *              Table for Diversity
-                * ant_div_alt_lnaconf          bit 0-1
-                * ant_div_main_lnaconf         bit 2-3
-                * ant_div_alt_gaintb           bit 4
-                * ant_div_main_gaintb          bit 5
-                * enable_ant_div_lnadiv        bit 6
-                * enable_ant_fast_div          bit 7
-                */
-               if ((ant_div_ctl1 >> 0x6) == 0x3)
+               if ((ant_div_ctl1 >> 0x6) == 0x3) {
                        pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+                       ath_info(common, "Enable LNA combining\n");
+               }
        }
 
        if (ath9k_hw_dfs_tested(ah))
@@ -2610,6 +2613,13 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
            ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
                        pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
 
+       /*
+        * Fast channel change across bands is available
+        * only for AR9462 and AR9565.
+        */
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+               pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH;
+
        return 0;
 }
 
index cd74b3a..38f461c 100644 (file)
@@ -247,6 +247,8 @@ enum ath9k_hw_caps {
        ATH9K_HW_CAP_DFS                        = BIT(16),
        ATH9K_HW_WOW_DEVICE_CAPABLE             = BIT(17),
        ATH9K_HW_CAP_PAPRD                      = BIT(18),
+       ATH9K_HW_CAP_FCC_BAND_SWITCH            = BIT(19),
+       ATH9K_HW_CAP_BT_ANT_DIV                 = BIT(20),
 };
 
 /*
@@ -310,6 +312,7 @@ struct ath9k_ops_config {
 
        /* Platform specific config */
        u32 xlna_gpio;
+       u32 ant_ctrl_comm2g_switch_enable;
        bool xatten_margin_cfg;
 };
 
@@ -716,7 +719,7 @@ struct ath_hw_ops {
                        struct ath_hw_antcomb_conf *antconf);
        void (*antdiv_comb_conf_set)(struct ath_hw *ah,
                        struct ath_hw_antcomb_conf *antconf);
-       void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
+       void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
        void (*spectral_scan_config)(struct ath_hw *ah,
                                     struct ath_spec_scan *param);
        void (*spectral_scan_trigger)(struct ath_hw *ah);
@@ -765,7 +768,6 @@ struct ath_hw {
        bool aspm_enabled;
        bool is_monitoring;
        bool need_an_top2_fixup;
-       bool shared_chain_lnadiv;
        u16 tx_trig_level;
 
        u32 nf_regs[6];
index 16f8b20..4afe30e 100644 (file)
@@ -53,9 +53,9 @@ static int ath9k_btcoex_enable;
 module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
 MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
 
-static int ath9k_enable_diversity;
-module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444);
-MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565");
+static int ath9k_bt_ant_diversity;
+module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444);
+MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity");
 
 bool is_ath9k_unloaded;
 /* We use the hw_value as an index into our private channel structure */
@@ -516,6 +516,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 static void ath9k_init_platform(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct ath_common *common = ath9k_hw_common(ah);
 
        if (common->bus_ops->ath_bus_type != ATH_PCI)
@@ -525,12 +526,21 @@ static void ath9k_init_platform(struct ath_softc *sc)
                               ATH9K_PCI_CUS230)) {
                ah->config.xlna_gpio = 9;
                ah->config.xatten_margin_cfg = true;
+               ah->config.ant_ctrl_comm2g_switch_enable = 0x000BBB88;
+               sc->ant_comb.low_rssi_thresh = 20;
+               sc->ant_comb.fast_div_bias = 3;
 
                ath_info(common, "Set parameters for %s\n",
                         (sc->driver_data & ATH9K_PCI_CUS198) ?
                         "CUS198" : "CUS230");
-       } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+       }
+
+       if (sc->driver_data & ATH9K_PCI_CUS217)
                ath_info(common, "CUS217 card detected\n");
+
+       if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) {
+               pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV;
+               ath_info(common, "Set BT/WLAN RX diversity capability\n");
        }
 }
 
@@ -584,6 +594,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 {
        struct ath9k_platform_data *pdata = sc->dev->platform_data;
        struct ath_hw *ah = NULL;
+       struct ath9k_hw_capabilities *pCap;
        struct ath_common *common;
        int ret = 0, i;
        int csz = 0;
@@ -600,6 +611,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ah->reg_ops.rmw = ath9k_reg_rmw;
        atomic_set(&ah->intr_ref_cnt, -1);
        sc->sc_ah = ah;
+       pCap = &ah->caps;
 
        sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET);
 
@@ -631,11 +643,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ath9k_init_platform(sc);
 
        /*
-        * Enable Antenna diversity only when BTCOEX is disabled
-        * and the user manually requests the feature.
+        * Enable WLAN/BT RX Antenna diversity only when:
+        *
+        * - BTCOEX is enabled
+        * - the user manually requests the feature.
+        * - the HW cap is set using the platform data.
         */
-       if (!common->btcoex_enabled && ath9k_enable_diversity)
-               common->antenna_diversity = 1;
+       if (common->btcoex_enabled && ath9k_bt_ant_diversity &&
+           (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
+               common->bt_ant_diversity = 1;
 
        spin_lock_init(&common->cc_lock);
 
index 1737a3e..afeab3c 100644 (file)
@@ -238,9 +238,6 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                ath_restart_work(sc);
        }
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
-               ath_ant_comb_update(sc);
-
        ieee80211_wake_queues(sc->hw);
 
        return true;
@@ -2094,7 +2091,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc,
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_wow_pattern *wow_pattern = NULL;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
+       struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
        int mask_len;
        s8 i = 0;
 
index c585c9b..76e8c35 100644 (file)
@@ -29,6 +29,14 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+       /* AR9285 card for Asus */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002B,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2C37),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+
        { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
        { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
        { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI   */
@@ -40,29 +48,101 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2086),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x1237),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2126),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
 
        /* PCI-E CUS230 */
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2152),
-         .driver_data = ATH9K_PCI_CUS230 },
+         .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_FOXCONN,
                         0xE075),
-         .driver_data = ATH9K_PCI_CUS230 },
+         .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
+
+       /* WB225 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3119),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3122),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x185F, /* WNC */
+                        0x3119),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x185F, /* WNC */
+                        0x3027),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x4105),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x4106),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410D),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410E),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410F),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC706),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC680),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC708),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3218),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3219),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
 
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
index 8b38030..4a1b992 100644 (file)
 #define AR_PHY_PLL_CONTROL 0x16180
 #define AR_PHY_PLL_MODE 0x16184
 
+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,
+};
+
 #endif
index 7eb1f4b..a3c4ca0 100644 (file)
@@ -1275,6 +1275,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct ath_softc *sc = priv;
@@ -1313,6 +1314,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed)
 {
index 9279927..52cd521 100644 (file)
@@ -1023,7 +1023,7 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
 }
 
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
-                            struct ath_tx_info *info, int len)
+                            struct ath_tx_info *info, int len, bool rts)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct sk_buff *skb;
@@ -1032,6 +1032,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
        const struct ieee80211_rate *rate;
        struct ieee80211_hdr *hdr;
        struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+       u32 rts_thresh = sc->hw->wiphy->rts_threshold;
        int i;
        u8 rix = 0;
 
@@ -1054,7 +1055,17 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
                rix = rates[i].idx;
                info->rates[i].Tries = rates[i].count;
 
-                   if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+               /*
+                * Handle RTS threshold for unaggregated HT frames.
+                */
+               if (bf_isampdu(bf) && !bf_isaggr(bf) &&
+                   (rates[i].flags & IEEE80211_TX_RC_MCS) &&
+                   unlikely(rts_thresh != (u32) -1)) {
+                       if (!rts_thresh || (len > rts_thresh))
+                               rts = true;
+               }
+
+               if (rts || rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                        info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
                        info->flags |= ATH9K_TXDESC_RTSENA;
                } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
@@ -1147,6 +1158,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_buf *bf_first = NULL;
        struct ath_tx_info info;
+       u32 rts_thresh = sc->hw->wiphy->rts_threshold;
+       bool rts = false;
 
        memset(&info, 0, sizeof(info));
        info.is_first = true;
@@ -1183,7 +1196,22 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                                info.flags |= (u32) bf->bf_state.bfs_paprd <<
                                              ATH9K_TXDESC_PAPRD_S;
 
-                       ath_buf_set_rate(sc, bf, &info, len);
+                       /*
+                        * mac80211 doesn't handle RTS threshold for HT because
+                        * the decision has to be taken based on AMPDU length
+                        * and aggregation is done entirely inside ath9k.
+                        * Set the RTS/CTS flag for the first subframe based
+                        * on the threshold.
+                        */
+                       if (aggr && (bf == bf_first) &&
+                           unlikely(rts_thresh != (u32) -1)) {
+                               /*
+                                * "len" is the size of the entire AMPDU.
+                                */
+                               if (!rts_thresh || (len > rts_thresh))
+                                       rts = true;
+                       }
+                       ath_buf_set_rate(sc, bf, &info, len, rts);
                }
 
                info.buf_addr[0] = bf->bf_buf_addr;
@@ -2168,7 +2196,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
                bf->bf_lastbf = bf;
                ath_set_rates(vif, NULL, bf);
-               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen, false);
                duration += info.rates[0].PktDuration;
                if (bf_tail)
                        bf_tail->bf_next = bf;
index f891d51..990dd42 100644 (file)
@@ -11,9 +11,6 @@ wil6210-y += txrx.o
 wil6210-y += debug.o
 wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
 
-ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
-       subdir-ccflags-y += -Werror
-endif
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
 
index ab63676..1caa319 100644 (file)
@@ -51,7 +51,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
                        if ((i % 64) == 0 && (i != 0))
                                seq_printf(s, "\n");
                        seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
-                                       "S" : (vring->ctx[i] ? "H" : "h"));
+                                       "S" : (vring->ctx[i].skb ? "H" : "h"));
                }
                seq_printf(s, "\n");
        }
@@ -406,7 +406,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
                volatile struct vring_tx_desc *d =
                                &(vring->va[dbg_txdesc_index].tx);
                volatile u32 *u = (volatile u32 *)d;
-               struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
+               struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
 
                seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
                seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
index 29dd1e5..717178f 100644 (file)
@@ -127,6 +127,8 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
 
        ndev->netdev_ops = &wil_netdev_ops;
        ndev->ieee80211_ptr = wdev;
+       ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
        wdev->netdev = ndev;
 
index eff1239..e59239d 100644 (file)
@@ -37,36 +37,40 @@ static inline void trace_ ## name(proto) {}
 #endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
 
 DECLARE_EVENT_CLASS(wil6210_wmi,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
 
-       TP_ARGS(id, buf, buf_len),
+       TP_ARGS(wmi, buf, buf_len),
 
        TP_STRUCT__entry(
+               __field(u8, mid)
                __field(u16, id)
+               __field(u32, timestamp)
                __field(u16, buf_len)
                __dynamic_array(u8, buf, buf_len)
        ),
 
        TP_fast_assign(
-               __entry->id = id;
+               __entry->mid = wmi->mid;
+               __entry->id = le16_to_cpu(wmi->id);
+               __entry->timestamp = le32_to_cpu(wmi->timestamp);
                __entry->buf_len = buf_len;
                memcpy(__get_dynamic_array(buf), buf, buf_len);
        ),
 
        TP_printk(
-               "id 0x%04x len %d",
-               __entry->id, __entry->buf_len
+               "MID %d id 0x%04x len %d timestamp %d",
+               __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
        )
 );
 
 DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
-       TP_ARGS(id, buf, buf_len)
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+       TP_ARGS(wmi, buf, buf_len)
 );
 
 DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
-       TP_ARGS(id, buf, buf_len)
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+       TP_ARGS(wmi, buf, buf_len)
 );
 
 #define WIL6210_MSG_MAX (200)
index d240b24..ea1abeb 100644 (file)
@@ -18,6 +18,9 @@
 #include <net/ieee80211_radiotap.h>
 #include <linux/if_arp.h>
 #include <linux/moduleparam.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
 
 #include "wil6210.h"
 #include "wmi.h"
@@ -70,7 +73,7 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 
        vring->swhead = 0;
        vring->swtail = 0;
-       vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+       vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL);
        if (!vring->ctx) {
                vring->va = NULL;
                return -ENOMEM;
@@ -108,39 +111,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
 
        while (!wil_vring_is_empty(vring)) {
                dma_addr_t pa;
-               struct sk_buff *skb;
                u16 dmalen;
+               struct wil_ctx *ctx;
 
                if (tx) {
                        struct vring_tx_desc dd, *d = &dd;
                        volatile struct vring_tx_desc *_d =
                                        &vring->va[vring->swtail].tx;
 
+                       ctx = &vring->ctx[vring->swtail];
                        *d = *_d;
                        pa = wil_desc_addr(&d->dma.addr);
                        dmalen = le16_to_cpu(d->dma.length);
-                       skb = vring->ctx[vring->swtail];
-                       if (skb) {
-                               dma_unmap_single(dev, pa, dmalen,
-                                                DMA_TO_DEVICE);
-                               dev_kfree_skb_any(skb);
-                               vring->ctx[vring->swtail] = NULL;
-                       } else {
+                       if (vring->ctx[vring->swtail].mapped_as_page) {
                                dma_unmap_page(dev, pa, dmalen,
                                               DMA_TO_DEVICE);
+                       } else {
+                               dma_unmap_single(dev, pa, dmalen,
+                                                DMA_TO_DEVICE);
                        }
+                       if (ctx->skb)
+                               dev_kfree_skb_any(ctx->skb);
                        vring->swtail = wil_vring_next_tail(vring);
                } else { /* rx */
                        struct vring_rx_desc dd, *d = &dd;
                        volatile struct vring_rx_desc *_d =
-                                       &vring->va[vring->swtail].rx;
+                                       &vring->va[vring->swhead].rx;
 
+                       ctx = &vring->ctx[vring->swhead];
                        *d = *_d;
                        pa = wil_desc_addr(&d->dma.addr);
                        dmalen = le16_to_cpu(d->dma.length);
-                       skb = vring->ctx[vring->swhead];
                        dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
-                       kfree_skb(skb);
+                       kfree_skb(ctx->skb);
                        wil_vring_advance_head(vring, 1);
                }
        }
@@ -187,7 +190,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
        d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
        d->dma.length = cpu_to_le16(sz);
        *_d = *d;
-       vring->ctx[i] = skb;
+       vring->ctx[i].skb = skb;
 
        return 0;
 }
@@ -352,11 +355,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
                return NULL;
        }
 
-       skb = vring->ctx[vring->swhead];
+       skb = vring->ctx[vring->swhead].skb;
        d = wil_skb_rxdesc(skb);
        *d = *_d;
        pa = wil_desc_addr(&d->dma.addr);
-       vring->ctx[vring->swhead] = NULL;
+       vring->ctx[vring->swhead].skb = NULL;
        wil_vring_advance_head(vring, 1);
 
        dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
@@ -407,6 +410,21 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
                return NULL;
        }
 
+       /* L4 IDENT is on when HW calculated checksum, check status
+        * and in case of error drop the packet
+        * higher stack layers will handle retransmission (if required)
+        */
+       if (d->dma.status & RX_DMA_STATUS_L4_IDENT) {
+               /* L4 protocol identified, csum calculated */
+               if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               } else {
+                       wil_err(wil, "Incorrect checksum reported\n");
+                       kfree_skb(skb);
+                       return NULL;
+               }
+       }
+
        ds_bits = wil_rxdesc_ds_bits(d);
        if (ds_bits == 1) {
                /*
@@ -646,6 +664,53 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
        return 0;
 }
 
+static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
+                               struct vring_tx_desc *d,
+                               struct sk_buff *skb)
+{
+       int protocol;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       switch (skb->protocol) {
+       case cpu_to_be16(ETH_P_IP):
+               protocol = ip_hdr(skb)->protocol;
+               break;
+       case cpu_to_be16(ETH_P_IPV6):
+               protocol = ipv6_hdr(skb)->nexthdr;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (protocol) {
+       case IPPROTO_TCP:
+               d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS);
+               /* L4 header len: TCP header length */
+               d->dma.d0 |=
+               (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+               break;
+       case IPPROTO_UDP:
+               /* L4 header len: UDP header length */
+               d->dma.d0 |=
+               (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       d->dma.ip_length = skb_network_header_len(skb);
+       d->dma.b11 = ETH_HLEN; /* MAC header length */
+       d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);
+       /* Enable TCP/UDP checksum */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS);
+       /* Calculate pseudo-header */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS);
+
+       return 0;
+}
+
 static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                        struct sk_buff *skb)
 {
@@ -655,7 +720,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        u32 swhead = vring->swhead;
        int avail = wil_vring_avail_tx(vring);
        int nr_frags = skb_shinfo(skb)->nr_frags;
-       uint f;
+       uint f = 0;
        int vring_index = vring - wil->vring_tx;
        uint i = swhead;
        dma_addr_t pa;
@@ -686,13 +751,20 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                return -EINVAL;
        /* 1-st segment */
        wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+       /* Process TCP/UDP checksum offloading */
+       if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
+               wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
+                       vring_index);
+               goto dma_error;
+       }
+
        d->mac.d[2] |= ((nr_frags + 1) <<
                       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
        if (nr_frags)
                *_d = *d;
 
        /* middle segments */
-       for (f = 0; f < nr_frags; f++) {
+       for (; f < nr_frags; f++) {
                const struct skb_frag_struct *frag =
                                &skb_shinfo(skb)->frags[f];
                int len = skb_frag_size(frag);
@@ -703,7 +775,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                if (unlikely(dma_mapping_error(dev, pa)))
                        goto dma_error;
                wil_tx_desc_map(d, pa, len, vring_index);
-               vring->ctx[i] = NULL;
+               vring->ctx[i].mapped_as_page = 1;
                *_d = *d;
        }
        /* for the last seg only */
@@ -712,6 +784,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
        *_d = *d;
 
+       /* hold reference to skb
+        * to prevent skb release before accounting
+        * in case of immediate "tx done"
+        */
+       vring->ctx[i].skb = skb_get(skb);
+
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
@@ -720,29 +798,31 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
        trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
        iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
-       /* hold reference to skb
-        * to prevent skb release before accounting
-        * in case of immediate "tx done"
-        */
-       vring->ctx[i] = skb_get(skb);
 
        return 0;
  dma_error:
        /* unmap what we have mapped */
-       /* Note: increment @f to operate with positive index */
-       for (f++; f > 0; f--) {
+       nr_frags = f + 1; /* frags mapped + one for skb head */
+       for (f = 0; f < nr_frags; f++) {
                u16 dmalen;
+               struct wil_ctx *ctx;
 
                i = (swhead + f) % vring->size;
+               ctx = &vring->ctx[i];
                _d = &(vring->va[i].tx);
                *d = *_d;
                _d->dma.status = TX_DMA_STATUS_DU;
                pa = wil_desc_addr(&d->dma.addr);
                dmalen = le16_to_cpu(d->dma.length);
-               if (vring->ctx[i])
-                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
-               else
+               if (ctx->mapped_as_page)
                        dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+               else
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
+               if (ctx->skb)
+                       dev_kfree_skb_any(ctx->skb);
+
+               memset(ctx, 0, sizeof(*ctx));
        }
 
        return -EINVAL;
@@ -821,8 +901,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                              &vring->va[vring->swtail].tx;
                struct vring_tx_desc dd, *d = &dd;
                dma_addr_t pa;
-               struct sk_buff *skb;
                u16 dmalen;
+               struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+               struct sk_buff *skb = ctx->skb;
 
                *d = *_d;
 
@@ -840,7 +921,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                  (const void *)d, sizeof(*d), false);
 
                pa = wil_desc_addr(&d->dma.addr);
-               skb = vring->ctx[vring->swtail];
+               if (ctx->mapped_as_page)
+                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+               else
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
                if (skb) {
                        if (d->dma.error == 0) {
                                ndev->stats.tx_packets++;
@@ -849,16 +934,15 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                ndev->stats.tx_errors++;
                        }
 
-                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
                        dev_kfree_skb_any(skb);
-                       vring->ctx[vring->swtail] = NULL;
-               } else {
-                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
                }
-               d->dma.addr.addr_low = 0;
-               d->dma.addr.addr_high = 0;
-               d->dma.length = 0;
-               d->dma.status = TX_DMA_STATUS_DU;
+               memset(ctx, 0, sizeof(*ctx));
+               /*
+                * There is no need to touch HW descriptor:
+                * - ststus bit TX_DMA_STATUS_DU is set by design,
+                *   so hardware will not try to process this desc.,
+                * - rest of descriptor will be initialized on Tx.
+                */
                vring->swtail = wil_vring_next_tail(vring);
                done++;
        }
index 859aea6..b382827 100644 (file)
@@ -235,7 +235,16 @@ struct vring_tx_mac {
 
 #define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
 #define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
-#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
+#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
+
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
 
 
 #define TX_DMA_STATUS_DU         BIT(0)
@@ -334,8 +343,17 @@ struct vring_rx_mac {
 
 #define RX_DMA_D0_CMD_DMA_IT     BIT(10)
 
+/* Error field, offload bits */
+#define RX_DMA_ERROR_L3_ERR   BIT(4)
+#define RX_DMA_ERROR_L4_ERR   BIT(5)
+
+
+/* Status field */
 #define RX_DMA_STATUS_DU         BIT(0)
 #define RX_DMA_STATUS_ERROR      BIT(2)
+
+#define RX_DMA_STATUS_L3_IDENT   BIT(4)
+#define RX_DMA_STATUS_L4_IDENT   BIT(5)
 #define RX_DMA_STATUS_PHY_INFO   BIT(6)
 
 struct vring_rx_dma {
index 44fdab5..c4a5163 100644 (file)
@@ -156,11 +156,22 @@ struct wil6210_mbox_hdr {
 /* max. value for wil6210_mbox_hdr.len */
 #define MAX_MBOXITEM_SIZE   (240)
 
+/**
+ * struct wil6210_mbox_hdr_wmi - WMI header
+ *
+ * @mid: MAC ID
+ *     00 - default, created by FW
+ *     01..0f - WiFi ports, driver to create
+ *     10..fe - debug
+ *     ff - broadcast
+ * @id: command/event ID
+ * @timestamp: FW fills for events, free-running msec timer
+ */
 struct wil6210_mbox_hdr_wmi {
-       u8 reserved0[2];
+       u8 mid;
+       u8 reserved;
        __le16 id;
-       __le16 info1; /* bits [0..3] - device_id, rest - unused */
-       u8 reserved1[2];
+       __le32 timestamp;
 } __packed;
 
 struct pending_wmi_event {
@@ -172,6 +183,14 @@ struct pending_wmi_event {
        } __packed event;
 };
 
+/**
+ * struct wil_ctx - software context for Vring descriptor
+ */
+struct wil_ctx {
+       struct sk_buff *skb;
+       u8 mapped_as_page:1;
+};
+
 union vring_desc;
 
 struct vring {
@@ -181,7 +200,7 @@ struct vring {
        u32 swtail;
        u32 swhead;
        u32 hwtail; /* write here to inform hw */
-       void **ctx; /* void *ctx[size] - software context */
+       struct wil_ctx *ctx; /* ctx[size] - software context */
 };
 
 enum { /* for wil6210_priv.status */
index dc8059a..5220f15 100644 (file)
@@ -172,8 +172,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
                        .len = cpu_to_le16(sizeof(cmd.wmi) + len),
                },
                .wmi = {
+                       .mid = 0,
                        .id = cpu_to_le16(cmdid),
-                       .info1 = 0,
                },
        };
        struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
@@ -248,7 +248,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
                  offsetof(struct wil6210_mbox_ctl, tx.head));
 
-       trace_wil6210_wmi_cmd(cmdid, buf, len);
+       trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
 
        /* interrupt to FW */
        iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -640,9 +640,13 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                            hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
-                       u16 id = le16_to_cpu(evt->event.wmi.id);
-                       wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
-                       trace_wil6210_wmi_event(id, &evt->event.wmi, len);
+                       struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
+                       u16 id = le16_to_cpu(wmi->id);
+                       u32 tstamp = le32_to_cpu(wmi->timestamp);
+                       wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
+                                   id, wmi->mid, tstamp);
+                       trace_wil6210_wmi_event(wmi, &wmi[1],
+                                               len - sizeof(*wmi));
                }
                wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
                                 &evt->event.hdr, sizeof(hdr) + len, true);
@@ -920,6 +924,12 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
                cmd.sniffer_cfg.phy_support =
                        cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
                                    ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+       } else {
+               /* Initialize offload (in non-sniffer mode).
+                * Linux IP stack always calculates IP checksum
+                * HW always calculate TCP/UDP checksum
+                */
+               cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
        }
        /* typical time for secure PCP is 840ms */
        rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
index bd98285..fa391e4 100644 (file)
@@ -928,9 +928,9 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                        }
                } else if (txs->phyerr) {
                        update_rate = false;
-                       brcms_err(wlc->hw->d11core,
-                                 "%s: ampdu tx phy error (0x%x)\n",
-                                 __func__, txs->phyerr);
+                       brcms_dbg_ht(wlc->hw->d11core,
+                                    "%s: ampdu tx phy error (0x%x)\n",
+                                    __func__, txs->phyerr);
                }
        }
 
index 9fd6f2f..7ca10bf 100644 (file)
@@ -882,8 +882,8 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
        mcl = le16_to_cpu(txh->MacTxControlLow);
 
        if (txs->phyerr)
-               brcms_err(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
-                         txs->phyerr, txh->MainRates);
+               brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
+                            txs->phyerr, txh->MainRates);
 
        if (txs->frameid != le16_to_cpu(txh->TxFrameID)) {
                brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n");
index 7afc613..48086e8 100644 (file)
@@ -832,7 +832,7 @@ struct wsm_tx {
        /* the MSDU shall be terminated. Overrides the global */
        /* dot11MaxTransmitMsduLifeTime setting [optional] */
        /* Device will set the default value if this is 0. */
-       u32 expire_time;
+       __le32 expire_time;
 
        /* WSM_HT_TX_... */
        __le32 ht_tx_parameters;
index 15f0fad..e4f56ad 100644 (file)
@@ -667,7 +667,7 @@ static int prism2_open(struct net_device *dev)
        if (local->no_pri) {
                printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
                       "f/w\n", dev->name);
-               return 1;
+               return -ENODEV;
        }
 
        if ((local->func->card_present && !local->func->card_present(local)) ||
@@ -682,7 +682,7 @@ static int prism2_open(struct net_device *dev)
                printk(KERN_WARNING "%s: could not enable MAC port\n",
                       dev->name);
                prism2_close(dev);
-               return 1;
+               return -ENODEV;
        }
        if (!local->dev_enabled)
                prism2_callback(local, PRISM2_CALLBACK_ENABLE);
index fe31590..aea667b 100644 (file)
@@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index c092033..f09e257 100644 (file)
@@ -475,6 +475,8 @@ il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header)
        }
 }
 
+#define SMALL_PACKET_SIZE 256
+
 static void
 il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                               struct ieee80211_rx_status *stats)
@@ -483,14 +485,13 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt);
        struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt);
        struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt);
-       u16 len = le16_to_cpu(rx_hdr->len);
+       u32 len = le16_to_cpu(rx_hdr->len);
        struct sk_buff *skb;
        __le16 fc = hdr->frame_control;
+       u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order;
 
        /* We received data from the HW, so stop the watchdog */
-       if (unlikely
-           (len + IL39_RX_FRAME_SIZE >
-            PAGE_SIZE << il->hw_params.rx_page_order)) {
+       if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) {
                D_DROP("Corruption detected!\n");
                return;
        }
@@ -506,26 +507,32 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                D_INFO("Woke queues - frame received on passive channel\n");
        }
 
-       skb = dev_alloc_skb(128);
+       skb = dev_alloc_skb(SMALL_PACKET_SIZE);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
                return;
        }
 
        if (!il3945_mod_params.sw_crypto)
-               il_set_decrypted_flag(il, (struct ieee80211_hdr *)rxb_addr(rxb),
+               il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt,
                                      le32_to_cpu(rx_end->status), stats);
 
-       skb_add_rx_frag(skb, 0, rxb->page,
-                       (void *)rx_hdr->payload - (void *)pkt, len,
-                       len);
-
+       /* If frame is small enough to fit into skb->head, copy it
+        * and do not consume a full page
+        */
+       if (len <= SMALL_PACKET_SIZE) {
+               memcpy(skb_put(skb, len), rx_hdr->payload, len);
+       } else {
+               skb_add_rx_frag(skb, 0, rxb->page,
+                               (void *)rx_hdr->payload - (void *)pkt, len,
+                               fraglen);
+               il->alloc_rxb_page--;
+               rxb->page = NULL;
+       }
        il_update_stats(il, false, fc, len);
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
        ieee80211_rx(il->hw, skb);
-       il->alloc_rxb_page--;
-       rxb->page = NULL;
 }
 
 #define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
index f2ed62e..b411ab9 100644 (file)
@@ -574,9 +574,11 @@ il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
        return decrypt_out;
 }
 
+#define SMALL_PACKET_SIZE 256
+
 static void
 il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
-                              u16 len, u32 ampdu_status, struct il_rx_buf *rxb,
+                              u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
                               struct ieee80211_rx_status *stats)
 {
        struct sk_buff *skb;
@@ -598,21 +600,25 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
                return;
 
-       skb = dev_alloc_skb(128);
+       skb = dev_alloc_skb(SMALL_PACKET_SIZE);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
                return;
        }
 
-       skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len,
-                       len);
+       if (len <= SMALL_PACKET_SIZE) {
+               memcpy(skb_put(skb, len), hdr, len);
+       } else {
+               skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
+                               len, PAGE_SIZE << il->hw_params.rx_page_order);
+               il->alloc_rxb_page--;
+               rxb->page = NULL;
+       }
 
        il_update_stats(il, false, fc, len);
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
        ieee80211_rx(il->hw, skb);
-       il->alloc_rxb_page--;
-       rxb->page = NULL;
 }
 
 /* Called for N_RX (legacy ABG frames), or
index ed3c42a..3ccbaf7 100644 (file)
@@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index cbaa5c2..e5c133e 100644 (file)
@@ -127,20 +127,3 @@ config IWLWIFI_DEVICE_TRACING
          If unsure, say Y so we can help you better when problems
          occur.
 endmenu
-
-config IWLWIFI_P2P
-       def_bool y
-       bool "iwlwifi experimental P2P support"
-       depends on IWLWIFI
-       help
-         This option enables experimental P2P support for some devices
-         based on microcode support. Since P2P support is still under
-         development, this option may even enable it for some devices
-         now that turn out to not support it in the future due to
-         microcode restrictions.
-
-         To determine if your microcode supports the experimental P2P
-         offered by this option, check if the driver advertises AP
-         support when it is loaded.
-
-         Say Y only if you want to experiment with P2P.
index 1835511..f2a86ff 100644 (file)
@@ -106,7 +106,6 @@ extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
 #define STATUS_CHANNEL_SWITCH_PENDING 11
 #define STATUS_SCAN_COMPLETE   12
 #define STATUS_POWER_PMI       13
-#define STATUS_SCAN_ROC_EXPIRED 14
 
 struct iwl_ucode_capabilities;
 
@@ -250,7 +249,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid);
 
 /* scan */
 void iwlagn_post_scan(struct iwl_priv *priv);
-void iwlagn_disable_roc(struct iwl_priv *priv);
 int iwl_force_rf_reset(struct iwl_priv *priv, bool external);
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
@@ -265,10 +263,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
                                   enum iwl_scan_type scan_type,
                                   enum ieee80211_band band);
 
-void iwl_scan_roc_expired(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb_status(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
  * ACTIVE_QUIET_TIME after sending probe request.  This shortens the dwell
index d532948..d94f8ab 100644 (file)
 } while (0)
 
 /* file operation */
-#define DEBUGFS_READ_FUNC(name)                                         \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file,               \
-                                       char __user *user_buf,          \
-                                       size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name)                                        \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file,              \
-                                       const char __user *user_buf,    \
-                                       size_t count, loff_t *ppos);
-
-
 #define DEBUGFS_READ_FILE_OPS(name)                                     \
-       DEBUGFS_READ_FUNC(name);                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .read = iwl_dbgfs_##name##_read,                                \
        .open = simple_open,                                            \
@@ -89,7 +77,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 };
 
 #define DEBUGFS_WRITE_FILE_OPS(name)                                    \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .open = simple_open,                                            \
@@ -98,8 +85,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 
 
 #define DEBUGFS_READ_WRITE_FILE_OPS(name)                               \
-       DEBUGFS_READ_FUNC(name);                                        \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
index 60a4e0d..a79fdd1 100644 (file)
@@ -540,7 +540,6 @@ struct iwl_rxon_context {
 enum iwl_scan_type {
        IWL_SCAN_NORMAL,
        IWL_SCAN_RADIO_RESET,
-       IWL_SCAN_ROC,
 };
 
 /**
@@ -825,12 +824,6 @@ struct iwl_priv {
        struct reply_tx_error_statistics reply_tx_stats;
        struct reply_agg_tx_error_statistics reply_agg_tx_stats;
 
-       /* remain-on-channel offload support */
-       struct ieee80211_channel *hw_roc_channel;
-       struct delayed_work hw_roc_disable_work;
-       int hw_roc_duration;
-       bool hw_roc_setup, hw_roc_start_notified;
-
        /* bt coex */
        u8 bt_enable_flag;
        u8 bt_status;
index 822f1a0..f0a2c95 100644 (file)
@@ -76,29 +76,6 @@ static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = {
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_GO) |
-                        BIT(NL80211_IFTYPE_AP),
-       },
-};
-
-static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = {
-       {
-               .max = 2,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT),
-       },
-};
-
 static const struct ieee80211_iface_combination
 iwlagn_iface_combinations_dualmode[] = {
        { .num_different_channels = 1,
@@ -114,21 +91,6 @@ iwlagn_iface_combinations_dualmode[] = {
        },
 };
 
-static const struct ieee80211_iface_combination
-iwlagn_iface_combinations_p2p[] = {
-       { .num_different_channels = 1,
-         .max_interfaces = 2,
-         .beacon_int_infra_match = true,
-         .limits = iwlagn_p2p_sta_go_limits,
-         .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits),
-       },
-       { .num_different_channels = 1,
-         .max_interfaces = 2,
-         .limits = iwlagn_p2p_2sta_limits,
-         .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits),
-       },
-};
-
 /*
  * Not a mac80211 entry point function, but it fits in with all the
  * other mac80211 functions grouped here.
@@ -186,19 +148,13 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 
        BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
 
-       if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) {
-               hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p;
-               hw->wiphy->n_iface_combinations =
-                       ARRAY_SIZE(iwlagn_iface_combinations_p2p);
-       } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+       if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
                hw->wiphy->iface_combinations =
                        iwlagn_iface_combinations_dualmode;
                hw->wiphy->n_iface_combinations =
                        ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
        }
 
-       hw->wiphy->max_remain_on_channel_duration = 500;
-
        hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
@@ -1156,126 +1112,6 @@ done:
        IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
-static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
-                                    struct ieee80211_vif *vif,
-                                    struct ieee80211_channel *channel,
-                                    int duration,
-                                    enum ieee80211_roc_type type)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
-       int err = 0;
-
-       if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
-               return -EOPNOTSUPP;
-
-       if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)))
-               return -EOPNOTSUPP;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-       mutex_lock(&priv->mutex);
-
-       if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-               /* mac80211 should not scan while ROC or ROC while scanning */
-               if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) {
-                       err = -EBUSY;
-                       goto out;
-               }
-
-               iwl_scan_cancel_timeout(priv, 100);
-
-               if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-                       err = -EBUSY;
-                       goto out;
-               }
-       }
-
-       priv->hw_roc_channel = channel;
-       /* convert from ms to TU */
-       priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024);
-       priv->hw_roc_start_notified = false;
-       cancel_delayed_work(&priv->hw_roc_disable_work);
-
-       if (!ctx->is_active) {
-               static const struct iwl_qos_info default_qos_data = {
-                       .def_qos_parm = {
-                               .ac[0] = {
-                                       .cw_min = cpu_to_le16(3),
-                                       .cw_max = cpu_to_le16(7),
-                                       .aifsn = 2,
-                                       .edca_txop = cpu_to_le16(1504),
-                               },
-                               .ac[1] = {
-                                       .cw_min = cpu_to_le16(7),
-                                       .cw_max = cpu_to_le16(15),
-                                       .aifsn = 2,
-                                       .edca_txop = cpu_to_le16(3008),
-                               },
-                               .ac[2] = {
-                                       .cw_min = cpu_to_le16(15),
-                                       .cw_max = cpu_to_le16(1023),
-                                       .aifsn = 3,
-                               },
-                               .ac[3] = {
-                                       .cw_min = cpu_to_le16(15),
-                                       .cw_max = cpu_to_le16(1023),
-                                       .aifsn = 7,
-                               },
-                       },
-               };
-
-               ctx->is_active = true;
-               ctx->qos_data = default_qos_data;
-               ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
-               memcpy(ctx->staging.node_addr,
-                      priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
-                      ETH_ALEN);
-               memcpy(ctx->staging.bssid_addr,
-                      priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
-                      ETH_ALEN);
-               err = iwlagn_commit_rxon(priv, ctx);
-               if (err)
-                       goto out;
-               ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK |
-                                            RXON_FILTER_PROMISC_MSK |
-                                            RXON_FILTER_CTL2HOST_MSK;
-
-               err = iwlagn_commit_rxon(priv, ctx);
-               if (err) {
-                       iwlagn_disable_roc(priv);
-                       goto out;
-               }
-               priv->hw_roc_setup = true;
-       }
-
-       err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band);
-       if (err)
-               iwlagn_disable_roc(priv);
-
- out:
-       mutex_unlock(&priv->mutex);
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return err;
-}
-
-static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
-       if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
-               return -EOPNOTSUPP;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-       mutex_lock(&priv->mutex);
-       iwl_scan_cancel_timeout(priv, priv->hw_roc_duration);
-       iwlagn_disable_roc(priv);
-       mutex_unlock(&priv->mutex);
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return 0;
-}
-
 static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     enum ieee80211_rssi_event rssi_event)
@@ -1431,12 +1267,8 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
                           viftype, vif->addr);
 
-       cancel_delayed_work_sync(&priv->hw_roc_disable_work);
-
        mutex_lock(&priv->mutex);
 
-       iwlagn_disable_roc(priv);
-
        if (!iwl_is_ready_rf(priv)) {
                IWL_WARN(priv, "Try to add interface when device not ready\n");
                err = -EINVAL;
@@ -1763,8 +1595,6 @@ struct ieee80211_ops iwlagn_hw_ops = {
        .channel_switch = iwlagn_mac_channel_switch,
        .flush = iwlagn_mac_flush,
        .tx_last_beacon = iwlagn_mac_tx_last_beacon,
-       .remain_on_channel = iwlagn_mac_remain_on_channel,
-       .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel,
        .rssi_callback = iwlagn_mac_rssi_callback,
        .set_tim = iwlagn_mac_set_tim,
 };
index 1531a4f..7aad766 100644 (file)
@@ -587,11 +587,6 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
        priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
                BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
 
-       if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P)
-               priv->contexts[IWL_RXON_CTX_PAN].interface_modes |=
-                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                       BIT(NL80211_IFTYPE_P2P_GO);
-
        priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
        priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
        priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
@@ -854,14 +849,6 @@ void iwl_down(struct iwl_priv *priv)
 
        iwl_scan_cancel_timeout(priv, 200);
 
-       /*
-        * If active, scanning won't cancel it, so say it expired.
-        * No race since we hold the mutex here and a new one
-        * can't come in at this time.
-        */
-       if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT)
-               ieee80211_remain_on_channel_expired(priv->hw);
-
        exit_pending =
                test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
@@ -1002,41 +989,6 @@ static void iwl_bg_restart(struct work_struct *data)
        }
 }
 
-
-
-
-void iwlagn_disable_roc(struct iwl_priv *priv)
-{
-       struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
-
-       lockdep_assert_held(&priv->mutex);
-
-       if (!priv->hw_roc_setup)
-               return;
-
-       ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
-       ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-
-       priv->hw_roc_channel = NULL;
-
-       memset(ctx->staging.node_addr, 0, ETH_ALEN);
-
-       iwlagn_commit_rxon(priv, ctx);
-
-       ctx->is_active = false;
-       priv->hw_roc_setup = false;
-}
-
-static void iwlagn_disable_roc_work(struct work_struct *work)
-{
-       struct iwl_priv *priv = container_of(work, struct iwl_priv,
-                                            hw_roc_disable_work.work);
-
-       mutex_lock(&priv->mutex);
-       iwlagn_disable_roc(priv);
-       mutex_unlock(&priv->mutex);
-}
-
 /*****************************************************************************
  *
  * driver setup and teardown
@@ -1053,8 +1005,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
        INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
        INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
-       INIT_DELAYED_WORK(&priv->hw_roc_disable_work,
-                         iwlagn_disable_roc_work);
 
        iwl_setup_scan_deferred_work(priv);
 
@@ -1082,7 +1032,6 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv)
 
        cancel_work_sync(&priv->bt_full_concurrency);
        cancel_work_sync(&priv->bt_runtime_config);
-       cancel_delayed_work_sync(&priv->hw_roc_disable_work);
 
        del_timer_sync(&priv->statistics_periodic);
        del_timer_sync(&priv->ucode_trace);
@@ -1169,12 +1118,6 @@ static void iwl_option_config(struct iwl_priv *priv)
 #else
        IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
 #endif
-
-#ifdef CONFIG_IWLWIFI_P2P
-       IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n");
-#else
-       IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n");
-#endif
 }
 
 static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
@@ -1315,10 +1258,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
 
        ucode_flags = fw->ucode_capa.flags;
 
-#ifndef CONFIG_IWLWIFI_P2P
-       ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
-#endif
-
        if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
                priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
                trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
@@ -1413,7 +1352,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
                 * if not PAN, then don't support P2P -- might be a uCode
                 * packaging bug or due to the eeprom check above
                 */
-               ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
                priv->sta_key_max_num = STA_KEY_MAX_NUM;
                trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
 
index 1b69394..b647e50 100644 (file)
@@ -2826,9 +2826,6 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i
 
        lq_sta->flush_timer = 0;
        lq_sta->supp_rates = sta->supp_rates[sband->band];
-       for (j = 0; j < LQ_SIZE; j++)
-               for (i = 0; i < IWL_RATE_COUNT; i++)
-                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
                       sta_id);
@@ -3319,7 +3316,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
-                        struct ieee80211_sta *sta, void *priv_sta)
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *priv_sta)
 {
 }
 static struct rate_control_ops rs_ops = {
index cd1ad00..d7ce2f1 100644 (file)
@@ -564,11 +564,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
        cmd.slots[0].type = 0; /* BSS */
        cmd.slots[1].type = 1; /* PAN */
 
-       if (priv->hw_roc_setup) {
-               /* both contexts must be used for this to happen */
-               slot1 = IWL_MIN_SLOT_TIME;
-               slot0 = 3000;
-       } else if (ctx_bss->vif && ctx_pan->vif) {
+       if (ctx_bss->vif && ctx_pan->vif) {
                int bcnint = ctx_pan->beacon_int;
                int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
 
index 8c686a5..35e0ee8 100644 (file)
@@ -100,9 +100,6 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
                ieee80211_scan_completed(priv->hw, aborted);
        }
 
-       if (priv->scan_type == IWL_SCAN_ROC)
-               iwl_scan_roc_expired(priv);
-
        priv->scan_type = IWL_SCAN_NORMAL;
        priv->scan_vif = NULL;
        priv->scan_request = NULL;
@@ -130,9 +127,6 @@ static void iwl_process_scan_complete(struct iwl_priv *priv)
                goto out_settings;
        }
 
-       if (priv->scan_type == IWL_SCAN_ROC)
-               iwl_scan_roc_expired(priv);
-
        if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
                int err;
 
@@ -284,12 +278,6 @@ static int iwl_rx_scan_start_notif(struct iwl_priv *priv,
                       le32_to_cpu(notif->tsf_low),
                       notif->status, notif->beacon_timer);
 
-       if (priv->scan_type == IWL_SCAN_ROC &&
-           !priv->hw_roc_start_notified) {
-               ieee80211_ready_on_channel(priv->hw);
-               priv->hw_roc_start_notified = true;
-       }
-
        return 0;
 }
 
@@ -697,8 +685,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
        scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
 
-       if (priv->scan_type != IWL_SCAN_ROC &&
-           iwl_is_any_associated(priv)) {
+       if (iwl_is_any_associated(priv)) {
                u16 interval = 0;
                u32 extra;
                u32 suspend_time = 100;
@@ -706,9 +693,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
                IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
                switch (priv->scan_type) {
-               case IWL_SCAN_ROC:
-                       WARN_ON(1);
-                       break;
                case IWL_SCAN_RADIO_RESET:
                        interval = 0;
                        break;
@@ -728,11 +712,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                scan->suspend_time = cpu_to_le32(scan_suspend_time);
                IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
                               scan_suspend_time, interval);
-       } else if (priv->scan_type == IWL_SCAN_ROC) {
-               scan->suspend_time = 0;
-               scan->max_out_time = 0;
-               scan->quiet_time = 0;
-               scan->quiet_plcp_th = 0;
        }
 
        switch (priv->scan_type) {
@@ -774,9 +753,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                } else
                        IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
                break;
-       case IWL_SCAN_ROC:
-               IWL_DEBUG_SCAN(priv, "Start ROC scan.\n");
-               break;
        }
 
        scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
@@ -898,7 +874,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                                        scan_cmd_size - sizeof(*scan));
                break;
        case IWL_SCAN_RADIO_RESET:
-       case IWL_SCAN_ROC:
                /* use bcast addr, will not be transmitted but must be valid */
                cmd_len = iwl_fill_probe_req(
                                        (struct ieee80211_mgmt *)scan->data,
@@ -926,46 +901,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                                is_active, n_probes,
                                (void *)&scan->data[cmd_len]);
                break;
-       case IWL_SCAN_ROC: {
-               struct iwl_scan_channel *scan_ch;
-               int n_chan, i;
-               u16 dwell;
-
-               dwell = iwl_limit_dwell(priv, priv->hw_roc_duration);
-               n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell);
-
-               scan->channel_count = n_chan;
-
-               scan_ch = (void *)&scan->data[cmd_len];
-
-               for (i = 0; i < n_chan; i++) {
-                       scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
-                       scan_ch->channel =
-                               cpu_to_le16(priv->hw_roc_channel->hw_value);
-
-                       if (i == n_chan - 1)
-                               dwell = priv->hw_roc_duration - i * dwell;
-
-                       scan_ch->active_dwell =
-                       scan_ch->passive_dwell = cpu_to_le16(dwell);
-
-                       /* Set txpower levels to defaults */
-                       scan_ch->dsp_atten = 110;
-
-                       /* NOTE: if we were doing 6Mb OFDM for scans we'd use
-                        * power level:
-                        * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
-                        */
-                       if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ)
-                               scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
-                       else
-                               scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
-                       scan_ch++;
-               }
-               }
-
-               break;
        }
 
        if (scan->channel_count == 0) {
@@ -1035,7 +970,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
 
        IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
                        scan_type == IWL_SCAN_NORMAL ? "" :
-                       scan_type == IWL_SCAN_ROC ? "remain-on-channel " :
                        "internal short ");
 
        set_bit(STATUS_SCANNING, &priv->status);
@@ -1149,40 +1083,3 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
                mutex_unlock(&priv->mutex);
        }
 }
-
-void iwl_scan_roc_expired(struct iwl_priv *priv)
-{
-       /*
-        * The status bit should be set here, to prevent a race
-        * where the atomic_read returns 1, but before the execution continues
-        * iwl_scan_offchannel_skb_status() checks if the status bit is set
-        */
-       set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
-
-       if (atomic_read(&priv->num_aux_in_flight) == 0) {
-               ieee80211_remain_on_channel_expired(priv->hw);
-               priv->hw_roc_channel = NULL;
-               schedule_delayed_work(&priv->hw_roc_disable_work,
-                                     10 * HZ);
-
-               clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
-       } else {
-               IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n",
-                              atomic_read(&priv->num_aux_in_flight));
-       }
-}
-
-void iwl_scan_offchannel_skb(struct iwl_priv *priv)
-{
-       WARN_ON(!priv->hw_roc_start_notified);
-       atomic_inc(&priv->num_aux_in_flight);
-}
-
-void iwl_scan_offchannel_skb_status(struct iwl_priv *priv)
-{
-       if (atomic_dec_return(&priv->num_aux_in_flight) == 0 &&
-           test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n");
-               iwl_scan_roc_expired(priv);
-       }
-}
index 5ee983f..3db0bbb 100644 (file)
@@ -478,9 +478,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
        if (sta_priv && sta_priv->client && !is_agg)
                atomic_inc(&sta_priv->pending_frames);
 
-       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-               iwl_scan_offchannel_skb(priv);
-
        return 0;
 
 drop_unlock_sta:
@@ -1158,7 +1155,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
        struct sk_buff *skb;
        struct iwl_rxon_context *ctx;
        bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
-       bool is_offchannel_skb;
 
        tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
                IWLAGN_TX_RES_TID_POS;
@@ -1178,8 +1174,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
 
        __skb_queue_head_init(&skbs);
 
-       is_offchannel_skb = false;
-
        if (tx_resp->frame_count == 1) {
                u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
                next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
@@ -1256,8 +1250,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                        if (!is_agg)
                                iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
 
-                       is_offchannel_skb =
-                               (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);
                        freed++;
                }
 
@@ -1271,14 +1263,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                if (!is_agg && freed != 1)
                        IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
 
-               /*
-                * An offchannel frame can be send only on the AUX queue, where
-                * there is no aggregation (and reordering) so it only is single
-                * skb is expected to be processed.
-                */
-               if (is_offchannel_skb && freed != 1)
-                       IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed);
-
                IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
                                   iwl_get_tx_fail_reason(status), status);
 
@@ -1298,9 +1282,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                ieee80211_tx_status_ni(priv->hw, skb);
        }
 
-       if (is_offchannel_skb)
-               iwl_scan_offchannel_skb_status(priv);
-
        return 0;
 }
 
index 22b7fa5..76e14c0 100644 (file)
@@ -99,6 +99,7 @@ static const struct iwl_base_params iwl7000_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = true,
+       .pcie_l1_allowed = true,
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
@@ -126,6 +127,16 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
        .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 };
 
+const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
+       .name = "Intel(R) Dual Band Wireless AC 7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+       .high_temp = true,
+};
+
 const struct iwl_cfg iwl7260_2n_cfg = {
        .name = "Intel(R) Dual Band Wireless N 7260",
        .fw_name_pre = IWL7260_FW_PRE,
index 83b9ff6..e4d370b 100644 (file)
@@ -152,6 +152,7 @@ struct iwl_base_params {
        unsigned int wd_timeout;
        u32 max_event_log_size;
        const bool shadow_reg_enable;
+       const bool pcie_l1_allowed;
 };
 
 /*
@@ -205,6 +206,7 @@ struct iwl_eeprom_params {
  * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
  * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
  * @internal_wimax_coex: internal wifi/wimax combo device
+ * @high_temp: Is this NIC is designated to be in high temperature.
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -233,6 +235,7 @@ struct iwl_cfg {
        enum iwl_led_mode led_mode;
        const bool rx_with_siso_diversity;
        const bool internal_wimax_coex;
+       bool high_temp;
 };
 
 /*
@@ -283,6 +286,7 @@ extern const struct iwl_cfg iwl135_bgn_cfg;
 #endif /* CONFIG_IWLDVM */
 #if IS_ENABLED(CONFIG_IWLMVM)
 extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp;
 extern const struct iwl_cfg iwl7260_2n_cfg;
 extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
index f844d5c..bd335f0 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
  * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ *     (rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
  */
 enum iwl_ucode_tlv_flag {
-       IWL_UCODE_TLV_FLAGS_PAN         = BIT(0),
-       IWL_UCODE_TLV_FLAGS_NEWSCAN     = BIT(1),
-       IWL_UCODE_TLV_FLAGS_MFP         = BIT(2),
-       IWL_UCODE_TLV_FLAGS_P2P         = BIT(3),
-       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
+       IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
+       IWL_UCODE_TLV_FLAGS_NEWSCAN             = BIT(1),
+       IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
+       IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
+       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
+       IWL_UCODE_TLV_FLAGS_UAPSD               = BIT(6),
+       IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
+       IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
+       IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
 };
 
 /* The default calibrate table size if not specified by firmware file */
index 305c81f..dfa4d2e 100644 (file)
@@ -33,6 +33,8 @@
 #include "iwl-io.h"
 #include "iwl-csr.h"
 #include "iwl-debug.h"
+#include "iwl-fh.h"
+#include "iwl-csr.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
@@ -166,3 +168,68 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
        }
 }
 IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
+
+static const char *get_fh_string(int cmd)
+{
+#define IWL_CMD(x) case x: return #x
+       switch (cmd) {
+       IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+       IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+       IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+       IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+       IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+       IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+       IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+       IWL_CMD(FH_TSSR_TX_STATUS_REG);
+       IWL_CMD(FH_TSSR_TX_ERROR_REG);
+       default:
+               return "UNKNOWN";
+       }
+#undef IWL_CMD
+}
+
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
+{
+       int i;
+       static const u32 fh_tbl[] = {
+               FH_RSCSR_CHNL0_STTS_WPTR_REG,
+               FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+               FH_RSCSR_CHNL0_WPTR,
+               FH_MEM_RCSR_CHNL0_CONFIG_REG,
+               FH_MEM_RSSR_SHARED_CTRL_REG,
+               FH_MEM_RSSR_RX_STATUS_REG,
+               FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+               FH_TSSR_TX_STATUS_REG,
+               FH_TSSR_TX_ERROR_REG
+       };
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (buf) {
+               int pos = 0;
+               size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return -ENOMEM;
+
+               pos += scnprintf(*buf + pos, bufsz - pos,
+                               "FH register values:\n");
+
+               for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+                       pos += scnprintf(*buf + pos, bufsz - pos,
+                               "  %34s: 0X%08x\n",
+                               get_fh_string(fh_tbl[i]),
+                               iwl_read_direct32(trans, fh_tbl[i]));
+
+               return pos;
+       }
+#endif
+
+       IWL_ERR(trans, "FH register values:\n");
+       for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
+               IWL_ERR(trans, "  %34s: 0X%08x\n",
+                       get_fh_string(fh_tbl[i]),
+                       iwl_read_direct32(trans, fh_tbl[i]));
+
+       return 0;
+}
index fd9f5b9..63d10ec 100644 (file)
@@ -77,4 +77,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 
+/* Error handling */
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
+
 #endif
index acd2665..b76a9a8 100644 (file)
@@ -118,6 +118,7 @@ static const u8 iwl_nvm_channels[] = {
 #define LAST_2GHZ_HT_PLUS      9
 #define LAST_5GHZ_HT           161
 
+#define DEFAULT_MAX_TX_POWER 16
 
 /* rate data (static) */
 static struct ieee80211_rate iwl_cfg80211_rates[] = {
@@ -232,8 +233,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
                /* Initialize regulatory-based run-time data */
 
-               /* TODO: read the real value from the NVM */
-               channel->max_power = 0;
+               /*
+                * Default value - highest tx power value.  max_power
+                * is not used in mvm, and is used for backwards compatibility
+                */
+               channel->max_power = DEFAULT_MAX_TX_POWER;
                is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
                IWL_DEBUG_EEPROM(dev,
                                 "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
index ff856e5..6d73817 100644 (file)
@@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM)   += iwlmvm.o
 iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
 iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
 iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o bt-coex.o
+iwlmvm-y += power.o power_legacy.o bt-coex.o
 iwlmvm-y += led.o tt.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
index dbd622a..0fad98b 100644 (file)
@@ -220,66 +220,87 @@ static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = {
 
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 {
-       struct iwl_bt_coex_cmd cmd = {
-               .max_kill = 5,
-               .bt3_time_t7_value = 1,
-               .bt3_prio_sample_time = 2,
-               .bt3_timer_t2_value = 0xc,
+       struct iwl_bt_coex_cmd *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+               .flags = CMD_SYNC,
        };
        int ret;
 
-       cmd.flags = iwlwifi_mod_params.bt_coex_active ?
+       /* go to CALIB state in internal BT-Coex state machine */
+       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
+                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
+                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->max_kill = 5;
+       bt_cmd->bt3_time_t7_value = 1;
+       bt_cmd->bt3_prio_sample_time = 2;
+       bt_cmd->bt3_timer_t2_value = 0xc;
+
+       bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
-       cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+       bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
 
-       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
-                                       BT_VALID_BT_PRIO_BOOST |
-                                       BT_VALID_MAX_KILL |
-                                       BT_VALID_3W_TMRS |
-                                       BT_VALID_KILL_ACK |
-                                       BT_VALID_KILL_CTS |
-                                       BT_VALID_REDUCED_TX_POWER |
-                                       BT_VALID_LUT);
+       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+                                           BT_VALID_BT_PRIO_BOOST |
+                                           BT_VALID_MAX_KILL |
+                                           BT_VALID_3W_TMRS |
+                                           BT_VALID_KILL_ACK |
+                                           BT_VALID_KILL_CTS |
+                                           BT_VALID_REDUCED_TX_POWER |
+                                           BT_VALID_LUT);
 
        if (mvm->cfg->bt_shared_single_ant)
-               memcpy(&cmd.decision_lut, iwl_single_shared_ant_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup,
                       sizeof(iwl_single_shared_ant_lookup));
        else if (is_loose_coex())
-               memcpy(&cmd.decision_lut, iwl_loose_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_loose_lookup,
                       sizeof(iwl_tight_lookup));
        else
-               memcpy(&cmd.decision_lut, iwl_tight_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_tight_lookup,
                       sizeof(iwl_tight_lookup));
 
-       cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
-       cmd.kill_ack_msk =
+       bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+       bt_cmd->kill_ack_msk =
                cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
-       cmd.kill_cts_msk =
+       bt_cmd->kill_cts_msk =
                cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
 
-       /* go to CALIB state in internal BT-Coex state machine */
-       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
-                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
-
-       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
-                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+       kfree(bt_cmd);
+       return ret;
 }
 
 static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                                           bool reduced_tx_power)
 {
        enum iwl_bt_kill_msk bt_kill_msk;
-       struct iwl_bt_coex_cmd cmd = {};
+       struct iwl_bt_coex_cmd *bt_cmd;
        struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .data[0] = &bt_cmd,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+               .flags = CMD_SYNC,
+       };
+       int ret = 0;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -308,24 +329,40 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                return 0;
 
        mvm->bt_kill_msk = bt_kill_msk;
-       cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
-       cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
-       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+       bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+       bt_cmd->valid_bit_msk =
+               cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
 
        IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
 }
 
 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
                                       bool enable)
 {
-       struct iwl_bt_coex_cmd cmd = {
-               .valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
-               .bt_reduced_tx_power = sta_id,
+       struct iwl_bt_coex_cmd *bt_cmd;
+       /* Send ASYNC since this can be sent from an atomic context */
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_DUP, },
+               .flags = CMD_ASYNC,
        };
+
        struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
+       int ret;
 
        /* This can happen if the station has been removed right now */
        if (sta_id == IWL_MVM_STATION_COUNT)
@@ -339,17 +376,26 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        if (mvmsta->bt_reduced_txpower == enable)
                return 0;
 
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
+       bt_cmd->bt_reduced_tx_power = sta_id;
+
        if (enable)
-               cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+               bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
 
        IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
                       enable ? "en" : "dis", sta_id);
 
        mvmsta->bt_reduced_txpower = enable;
 
-       /* Send ASYNC since this can be sent from an atomic context */
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
-                                   sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
 }
 
 struct iwl_bt_iterator_data {
@@ -384,6 +430,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
 
        smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       /* non associated BSSes aren't to be considered */
+       if (!vif->bss_conf.assoc)
+               return;
+
        if (band != IEEE80211_BAND_2GHZ) {
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
                                    smps_mode);
@@ -523,6 +573,8 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
                                        lockdep_is_held(&mvm->mutex));
        mvmsta = (void *)sta->drv_priv;
 
+       data->num_bss_ifaces++;
+
        /*
         * This interface doesn't support reduced Tx power (because of low
         * RSSI probably), then set bt_kill_msk to default values.
@@ -588,23 +640,5 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
 void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       enum ieee80211_band band;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(vif->chanctx_conf);
-       if (chanctx_conf && chanctx_conf->def.chan)
-               band = chanctx_conf->def.chan->band;
-       else
-               band = -1;
-       rcu_read_unlock();
-
-       /* if we are in 2GHz we will get a notification from the fw */
-       if (band == IEEE80211_BAND_2GHZ)
-               return;
-
-       /* else, we can remove all the constraints */
-       memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
-
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
new file mode 100644 (file)
index 0000000..64656e0
--- /dev/null
@@ -0,0 +1,71 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __MVM_CONSTANTS_H
+#define __MVM_CONSTANTS_H
+
+#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
+#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
+
+#endif /* __MVM_CONSTANTS_H */
index 83da884..d0d7a20 100644 (file)
@@ -105,7 +105,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
        list_for_each_entry(ifa, &idev->addr_list, if_list) {
                mvmvif->target_ipv6_addrs[idx] = ifa->addr;
                idx++;
-               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
+               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
                        break;
        }
        read_unlock_bh(&idev->lock);
@@ -378,36 +378,68 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
 static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif)
 {
-       struct iwl_proto_offload_cmd cmd = {};
+       union {
+               struct iwl_proto_offload_cmd_v1 v1;
+               struct iwl_proto_offload_cmd_v2 v2;
+       } cmd = {};
+       struct iwl_proto_offload_cmd_common *common;
+       u32 enabled = 0, size;
 #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
 
-       if (mvmvif->num_target_ipv6_addrs) {
-               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
-               memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
-       }
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+               if (mvmvif->num_target_ipv6_addrs) {
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+                       memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
+               }
+
+               BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
+                            sizeof(mvmvif->target_ipv6_addrs[0]));
+
+               for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+                                   IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
+                       memcpy(cmd.v2.target_ipv6_addr[i],
+                              &mvmvif->target_ipv6_addrs[i],
+                              sizeof(cmd.v2.target_ipv6_addr[i]));
+       } else {
+               if (mvmvif->num_target_ipv6_addrs) {
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+                       memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
+               }
 
-       BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
-                    sizeof(mvmvif->target_ipv6_addrs[i]));
+               BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
+                            sizeof(mvmvif->target_ipv6_addrs[0]));
 
-       for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
-               memcpy(cmd.target_ipv6_addr[i],
-                      &mvmvif->target_ipv6_addrs[i],
-                      sizeof(cmd.target_ipv6_addr[i]));
+               for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+                                   IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
+                       memcpy(cmd.v1.target_ipv6_addr[i],
+                              &mvmvif->target_ipv6_addrs[i],
+                              sizeof(cmd.v1.target_ipv6_addr[i]));
+       }
 #endif
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+               common = &cmd.v2.common;
+               size = sizeof(cmd.v2);
+       } else {
+               common = &cmd.v1.common;
+               size = sizeof(cmd.v1);
+       }
+
        if (vif->bss_conf.arp_addr_cnt) {
-               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
-               cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
-               memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
+               enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
+               common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+               memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
        }
 
-       if (!cmd.enabled)
+       if (!enabled)
                return 0;
 
+       common->enabled = cpu_to_le32(enabled);
+
        return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+                                   size, &cmd);
 }
 
 enum iwl_mvm_tcp_packet_type {
index c24a744..56f6827 100644 (file)
@@ -424,40 +424,11 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
        struct ieee80211_vif *vif = file->private_data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->dbgfs_data;
-       struct iwl_powertable_cmd cmd = {};
        char buf[256];
        int bufsz = sizeof(buf);
-       int pos = 0;
+       int pos;
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-
-       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
-                        (cmd.flags &
-                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
-                        0 : 1);
-       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
-                        le32_to_cpu(cmd.skip_dtim_periods));
-       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
-                        iwlmvm_mod_params.power_scheme);
-       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
-                        le16_to_cpu(cmd.flags));
-       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
-                        cmd.keep_alive_seconds);
-
-       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
-                                (cmd.flags &
-                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
-                                1 : 0);
-               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.rx_data_timeout));
-               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.tx_data_timeout));
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "lprx_rssi_threshold = %d\n",
-                                        le32_to_cpu(cmd.lprx_rssi_threshold));
-       }
+       pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -626,20 +597,19 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
                                          size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       bool restart_fw = iwlwifi_mod_params.restart_fw;
        int ret;
 
-       iwlwifi_mod_params.restart_fw = true;
-
        mutex_lock(&mvm->mutex);
 
+       /* allow one more restart that we're provoking here */
+       if (mvm->restart_fw >= 0)
+               mvm->restart_fw++;
+
        /* take the return value to make compiler happy - it will fail anyway */
        ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
 
        mutex_unlock(&mvm->mutex);
 
-       iwlwifi_mod_params.restart_fw = restart_fw;
-
        return count;
 }
 
@@ -661,8 +631,14 @@ static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
        case MVM_DEBUGFS_BF_ROAMING_STATE:
                dbgfs_bf->bf_roaming_state = value;
                break;
-       case MVM_DEBUGFS_BF_TEMPERATURE_DELTA:
-               dbgfs_bf->bf_temperature_delta = value;
+       case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
+               dbgfs_bf->bf_temp_threshold = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
+               dbgfs_bf->bf_temp_fast_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
+               dbgfs_bf->bf_temp_slow_filter = value;
                break;
        case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
                dbgfs_bf->bf_enable_beacon_filter = value;
@@ -721,13 +697,27 @@ static ssize_t iwl_dbgfs_bf_params_write(struct file *file,
                    value > IWL_BF_ROAMING_STATE_MAX)
                        return -EINVAL;
                param = MVM_DEBUGFS_BF_ROAMING_STATE;
-       } else if (!strncmp("bf_temperature_delta=", buf, 21)) {
-               if (sscanf(buf+21, "%d", &value) != 1)
+       } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
+               if (sscanf(buf+18, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
+                   value > IWL_BF_TEMP_THRESHOLD_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+       } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
+                   value > IWL_BF_TEMP_FAST_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
+       } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
                        return -EINVAL;
-               if (value < IWL_BF_TEMPERATURE_DELTA_MIN ||
-                   value > IWL_BF_TEMPERATURE_DELTA_MAX)
+               if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
+                   value > IWL_BF_TEMP_SLOW_FILTER_MAX)
                        return -EINVAL;
-               param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA;
+               param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
        } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
                if (sscanf(buf+24, "%d", &value) != 1)
                        return -EINVAL;
@@ -789,41 +779,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
        int pos = 0;
        const size_t bufsz = sizeof(buf);
        struct iwl_beacon_filter_cmd cmd = {
-               .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,
-               .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT,
-               .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,
-               .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,
-               .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT,
-               .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,
-               .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),
-               .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT),
-               .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT,
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter =
+                       cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
+               .ba_enable_beacon_abort =
+                       cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
        };
 
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
        if (mvmvif->bf_enabled)
-               cmd.bf_enable_beacon_filter = 1;
+               cmd.bf_enable_beacon_filter = cpu_to_le32(1);
        else
                cmd.bf_enable_beacon_filter = 0;
 
        pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
-                        cmd.bf_energy_delta);
+                        le32_to_cpu(cmd.bf_energy_delta));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
-                        cmd.bf_roaming_energy_delta);
+                        le32_to_cpu(cmd.bf_roaming_energy_delta));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
-                        cmd.bf_roaming_state);
-       pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n",
-                        cmd.bf_temperature_delta);
+                        le32_to_cpu(cmd.bf_roaming_state));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
+                        le32_to_cpu(cmd.bf_temp_threshold));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_fast_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_slow_filter));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
-                        cmd.bf_enable_beacon_filter);
+                        le32_to_cpu(cmd.bf_enable_beacon_filter));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
-                        cmd.bf_debug_flag);
+                        le32_to_cpu(cmd.bf_debug_flag));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
-                        cmd.bf_escape_timer);
+                        le32_to_cpu(cmd.bf_escape_timer));
        pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
-                        cmd.ba_escape_timer);
+                        le32_to_cpu(cmd.ba_escape_timer));
        pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
-                        cmd.ba_enable_beacon_abort);
+                        le32_to_cpu(cmd.ba_enable_beacon_abort));
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
index 6f8b2c1..df72fcd 100644 (file)
@@ -98,34 +98,63 @@ enum iwl_proto_offloads {
        IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
 };
 
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS       2
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1    2
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2    6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   6
 
 /**
- * struct iwl_proto_offload_cmd - ARP/NS offload configuration
+ * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
  * @enabled: enable flags
  * @remote_ipv4_addr: remote address to answer to (or zero if all)
  * @host_ipv4_addr: our IPv4 address to respond to queries for
  * @arp_mac_addr: our MAC address for ARP responses
- * @remote_ipv6_addr: remote address to answer to (or zero if all)
- * @solicited_node_ipv6_addr: broken -- solicited node address exists
- *     for each target address
- * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @reserved: unused
  */
-struct iwl_proto_offload_cmd {
+struct iwl_proto_offload_cmd_common {
        __le32 enabled;
        __be32 remote_ipv4_addr;
        __be32 host_ipv4_addr;
        u8 arp_mac_addr[ETH_ALEN];
-       __le16 reserved1;
+       __le16 reserved;
+} __packed;
 
+/**
+ * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v1 {
+       struct iwl_proto_offload_cmd_common common;
        u8 remote_ipv6_addr[16];
        u8 solicited_node_ipv6_addr[16];
-       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16];
        u8 ndp_mac_addr[ETH_ALEN];
        __le16 reserved2;
 } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
 
+/**
+ * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v2 {
+       struct iwl_proto_offload_cmd_common common;
+       u8 remote_ipv6_addr[16];
+       u8 solicited_node_ipv6_addr[16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16];
+       u8 ndp_mac_addr[ETH_ALEN];
+       u8 numValidIPv6Addresses;
+       u8 reserved2[3];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
+
 
 /*
  * WOWLAN_PATTERNS
index a6da359..060e630 100644 (file)
  *             '1' Driver enables PM (use rest of parameters)
  * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
  *             '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ *             access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ *             PBW Snoozing enabled
  * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
  * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
 */
@@ -86,6 +90,8 @@ enum iwl_power_flags {
        POWER_FLAGS_POWER_SAVE_ENA_MSK          = BIT(0),
        POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK    = BIT(1),
        POWER_FLAGS_SKIP_OVER_DTIM_MSK          = BIT(2),
+       POWER_FLAGS_SNOOZE_ENA_MSK              = BIT(5),
+       POWER_FLAGS_BT_SCO_ENA                  = BIT(8),
        POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(9),
        POWER_FLAGS_LPRX_ENA_MSK                = BIT(11),
 };
@@ -93,7 +99,8 @@ enum iwl_power_flags {
 #define IWL_POWER_VEC_SIZE 5
 
 /**
- * struct iwl_powertable_cmd - Power Table Command
+ * struct iwl_powertable_cmd - legacy power command. Beside old API support this
+ *     is used also with a new power API for device wide power settings.
  * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
  *
  * @flags:             Power table command flags from POWER_FLAGS_*
@@ -125,6 +132,72 @@ struct iwl_powertable_cmd {
 } __packed;
 
 /**
+ * struct iwl_mac_power_cmd - New power command containing uAPSD support
+ * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
+ * @id_and_color:      MAC contex identifier
+ * @flags:             Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds:        Keep alive period in seconds. Default - 25 sec.
+ *                     Minimum allowed:- 3 * DTIM. Keep alive period must be
+ *                     set regardless of power scheme or current power state.
+ *                     FW use this value also when PM is disabled.
+ * @rx_data_timeout:    Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - legacy PM
+ * @tx_data_timeout:    Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - legacy PM
+ * @sleep_interval:    not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ *                     is set. For example, if it is required to skip over
+ *                     one DTIM, this value need to be set to 2 (DTIM periods).
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ *                     Default: 80dbm
+ * @num_skip_dtim:     Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval:   TBD
+ * @snooze_window:     TBD
+ * @snooze_step:       TBD
+ * @qndp_tid:          TID client shall use for uAPSD QNDP triggers
+ * @uapsd_ac_flags:    Set trigger-enabled and delivery-enabled indication for
+ *                     each corresponding AC.
+ *                     Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
+ * @uapsd_max_sp:      Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
+ *                     values.
+ * @heavy_traffic_thr_tx_pkts: TX threshold measured in number of packets
+ * @heavy_traffic_thr_rx_pkts: RX threshold measured in number of packets
+ * @heavy_traffic_thr_tx_load: TX threshold measured in load's percentage
+ * @heavy_traffic_thr_rx_load: RX threshold measured in load's percentage
+ * @limited_ps_threshold:
+*/
+struct iwl_mac_power_cmd {
+       /* CONTEXT_DESC_API_T_VER_1 */
+       __le32 id_and_color;
+
+       /* CLIENT_PM_POWER_TABLE_S_VER_1 */
+       __le16 flags;
+       __le16 keep_alive_seconds;
+       __le32 rx_data_timeout;
+       __le32 tx_data_timeout;
+       __le32 rx_data_timeout_uapsd;
+       __le32 tx_data_timeout_uapsd;
+       u8 lprx_rssi_threshold;
+       u8 skip_dtim_periods;
+       __le16 snooze_interval;
+       __le16 snooze_window;
+       u8 snooze_step;
+       u8 qndp_tid;
+       u8 uapsd_ac_flags;
+       u8 uapsd_max_sp;
+       u8 heavy_traffic_threshold_tx_packets;
+       u8 heavy_traffic_threshold_rx_packets;
+       u8 heavy_traffic_threshold_tx_percentage;
+       u8 heavy_traffic_threshold_rx_percentage;
+       u8 limited_ps_threshold;
+       u8 reserved;
+} __packed;
+
+/**
  * struct iwl_beacon_filter_cmd
  * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
  * @id_and_color: MAC contex identifier
@@ -143,11 +216,21 @@ struct iwl_powertable_cmd {
  *      calculated for current beacon is less than the threshold, use
  *      Roaming Energy Delta Threshold, otherwise use normal Energy Delta
  *      Threshold. Typical energy threshold is -72dBm.
- * @bf_temperature_delta: Send Beacon to driver if delta in temperature values
- *      calculated for this and the last passed beacon is greater than  this
- *      threshold. Zero value means that the temperature changeis ignored for
+ * @bf_temp_threshold: This threshold determines the type of temperature
+ *     filtering (Slow or Fast) that is selected (Units are in Celsuis):
+ *      If the current temperature is above this threshold - Fast filter
+ *     will be used, If the current temperature is below this threshold -
+ *     Slow filter will be used.
+ * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values
+ *      calculated for this and the last passed beacon is greater than this
+ *      threshold. Zero value means that the temperature change is ignored for
  *      beacon filtering; beacons will not be  forced to be sent to driver
  *      regardless of whether its temerature has been changed.
+ * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
+ *      calculated for this and the last passed beacon is greater than this
+ *      threshold. Zero value means that the temperature change is ignored for
+ *      beacon filtering; beacons will not be forced to be sent to driver
+ *      regardless of whether its temerature has been changed.
  * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
  * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
  *      for a specific period of time. Units: Beacons.
@@ -156,17 +239,17 @@ struct iwl_powertable_cmd {
  * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
  */
 struct iwl_beacon_filter_cmd {
-       u8 bf_energy_delta;
-       u8 bf_roaming_energy_delta;
-       u8 bf_roaming_state;
-       u8 bf_temperature_delta;
-       u8 bf_enable_beacon_filter;
-       u8 bf_debug_flag;
-       __le16 reserved1;
+       __le32 bf_energy_delta;
+       __le32 bf_roaming_energy_delta;
+       __le32 bf_roaming_state;
+       __le32 bf_temp_threshold;
+       __le32 bf_temp_fast_filter;
+       __le32 bf_temp_slow_filter;
+       __le32 bf_enable_beacon_filter;
+       __le32 bf_debug_flag;
        __le32 bf_escape_timer;
        __le32 ba_escape_timer;
-       u8 ba_enable_beacon_abort;
-       u8 reserved2[3];
+       __le32 ba_enable_beacon_abort;
 } __packed;
 
 /* Beacon filtering and beacon abort */
@@ -182,9 +265,17 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ROAMING_STATE_MAX 255
 #define IWL_BF_ROAMING_STATE_MIN 0
 
-#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5
-#define IWL_BF_TEMPERATURE_DELTA_MAX 255
-#define IWL_BF_TEMPERATURE_DELTA_MIN 0
+#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
+#define IWL_BF_TEMP_THRESHOLD_MAX 255
+#define IWL_BF_TEMP_THRESHOLD_MIN 0
+
+#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
+#define IWL_BF_TEMP_FAST_FILTER_MAX 255
+#define IWL_BF_TEMP_FAST_FILTER_MIN 0
+
+#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
+#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
+#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
 
 #define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
 
@@ -194,19 +285,23 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ESCAPE_TIMER_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
-#define IWL_BA_ESCAPE_TIMER_DEFAULT 3
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D3 6
 #define IWL_BA_ESCAPE_TIMER_MAX 1024
 #define IWL_BA_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
 
-#define IWL_BF_CMD_CONFIG_DEFAULTS                                     \
-       .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,                 \
-       .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \
-       .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,               \
-       .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,       \
-       .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,                     \
-       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),    \
+#define IWL_BF_CMD_CONFIG_DEFAULTS                                          \
+       .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT),         \
+       .bf_roaming_energy_delta =                                           \
+               cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT),            \
+       .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT),       \
+       .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT),     \
+       .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \
+       .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \
+       .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT),             \
+       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),         \
        .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
 
 #endif
index 365095a..83cb9b9 100644 (file)
@@ -137,6 +137,8 @@ struct iwl_ssid_ie {
  *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
  *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
  *@SCAN_FLAGS_FRAGMENTED_SCAN:
+ *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
+ *     in the past hour, even if they are marked as passive.
  */
 enum iwl_scan_flags {
        SCAN_FLAGS_PERIODIC_SCAN                = BIT(0),
@@ -144,6 +146,7 @@ enum iwl_scan_flags {
        SCAN_FLAGS_DELAYED_SCAN_LOWBAND         = BIT(2),
        SCAN_FLAGS_DELAYED_SCAN_HIGHBAND        = BIT(3),
        SCAN_FLAGS_FRAGMENTED_SCAN              = BIT(4),
+       SCAN_FLAGS_PASSIVE2ACTIVE               = BIT(5),
 };
 
 /**
@@ -178,7 +181,7 @@ enum iwl_scan_type {
  * @quiet_time: in msecs, dwell this time for active scan on quiet channels
  * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
  *     this number of packets were received (typically 1)
- * @passive2active: is auto switching from passive to active allowed (0 or 1)
+ * @passive2active: is auto switching from passive to active during scan allowed
  * @rxchain_sel_flags: RXON_RX_CHAIN_*
  * @max_out_time: in usecs, max out of serving channel time
  * @suspend_time: how long to pause scan when returning to service channel:
index 700cce7..d606197 100644 (file)
@@ -91,7 +91,6 @@
  * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
  * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
  * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
- * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
  * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
  * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
  * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
@@ -120,7 +119,6 @@ enum iwl_tx_flags {
        TX_CMD_FLG_RESP_TO_DRV          = BIT(21),
        TX_CMD_FLG_CCMP_AGG             = BIT(22),
        TX_CMD_FLG_TKIP_MIC_DONE        = BIT(23),
-       TX_CMD_FLG_CTS_ONLY             = BIT(24),
        TX_CMD_FLG_DUR                  = BIT(25),
        TX_CMD_FLG_FW_DROP              = BIT(26),
        TX_CMD_FLG_EXEC_PAPD            = BIT(27),
index cbfb3be..55854a3 100644 (file)
@@ -136,7 +136,7 @@ enum {
        CALIB_RES_NOTIF_PHY_DB = 0x6b,
        /* PHY_DB_CMD = 0x6c, */
 
-       /* Power */
+       /* Power - legacy power table command */
        POWER_TABLE_CMD = 0x77,
 
        /* Thermal Throttling*/
@@ -159,6 +159,7 @@ enum {
        TX_ANT_CONFIGURATION_CMD = 0x98,
        BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
+       REDUCE_TX_POWER_CMD = 0x9f,
 
        /* RF-KILL commands and notifications */
        CARD_STATE_CMD = 0xa0,
@@ -166,6 +167,9 @@ enum {
 
        MISSED_BEACONS_NOTIFICATION = 0xa2,
 
+       /* Power - new power table command */
+       MAC_PM_POWER_TABLE = 0xa9,
+
        REPLY_RX_PHY_CMD = 0xc0,
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
@@ -223,6 +227,19 @@ struct iwl_tx_ant_cfg_cmd {
        __le32 valid;
 } __packed;
 
+/**
+ * struct iwl_reduce_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @flags: (reserved for future implementation)
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in dBms.
+ */
+struct iwl_reduce_tx_power_cmd {
+       u8 flags;
+       u8 mac_context_id;
+       __le16 pwr_restriction;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
+
 /*
  * Calibration control struct.
  * Sent as part of the phy configuration command.
@@ -765,6 +782,14 @@ struct iwl_phy_context_cmd {
 } __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
 
 #define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
+#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
+#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
+#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
+#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
+#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
+#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+
 #define IWL_RX_INFO_AGC_IDX 1
 #define IWL_RX_INFO_RSSI_AB_IDX 2
 #define IWL_OFDM_AGC_A_MSK 0x0000007f
index cd7c003..c76299a 100644 (file)
 
 #define UCODE_VALID_OK cpu_to_le32(0x1)
 
-/* Default calibration values for WkP - set to INIT image w/o running */
-static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
-static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
-
-struct iwl_calib_default_data {
-       u16 size;
-       void *data;
-};
-
-#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
-
-static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
-       [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
-       [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
-};
-
 struct iwl_mvm_alive_data {
        bool valid;
        u32 scd_base_addr;
@@ -248,40 +232,6 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
                                    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
 }
 
-static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
-{
-       u8 cmd_raw[16]; /* holds the variable size commands */
-       struct iwl_set_calib_default_cmd *cmd =
-               (struct iwl_set_calib_default_cmd *)cmd_raw;
-       int ret, i;
-
-       /* Setting default values for calibrations we don't run */
-       for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
-               u16 cmd_len;
-
-               if (wkp_calib_default_data[i].size == 0)
-                       continue;
-
-               memset(cmd_raw, 0, sizeof(cmd_raw));
-               cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
-               cmd->calib_index = cpu_to_le16(i);
-               cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
-               if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
-                             "Need to enlarge cmd_raw to %d\n", cmd_len))
-                       break;
-               memcpy(cmd->data, wkp_calib_default_data[i].data,
-                      wkp_calib_default_data[i].size);
-               ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
-                                          sizeof(*cmd) +
-                                          wkp_calib_default_data[i].size,
-                                          cmd);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
 int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 {
        struct iwl_notification_wait calib_wait;
@@ -342,11 +292,6 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        if (ret)
                goto error;
 
-       /* need to set default values */
-       ret = iwl_set_default_calibrations(mvm);
-       if (ret)
-               goto error;
-
        /*
         * Send phy configurations command to init uCode
         * to start the 16.0 uCode init image internal calibrations.
index 94aae9c..5fe23a5 100644 (file)
@@ -264,7 +264,8 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                return 0;
 
        /* Therefore, in recovery, we can't get here */
-       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+       if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
+               return -EBUSY;
 
        mvmvif->id = find_first_bit(data.available_mac_ids,
                                    NUM_MAC_INDEX_DRIVER);
index f19baf0..785c782 100644 (file)
@@ -153,7 +153,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
-                   IEEE80211_HW_CONNECTION_MONITOR;
+                   IEEE80211_HW_CONNECTION_MONITOR |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+                   IEEE80211_HW_SUPPORTS_STATIC_SMPS;
 
        hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -552,6 +554,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                        goto out_release;
                }
 
+               iwl_mvm_vif_dbgfs_register(mvm, vif);
                goto out_unlock;
        }
 
@@ -567,7 +570,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 
        /* beacon filtering */
        if (!mvm->bf_allowed_vif &&
-           vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
        }
@@ -719,6 +723,20 @@ out_release:
        mutex_unlock(&mvm->mutex);
 }
 
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               s8 tx_power)
+{
+       /* FW is in charge of regulatory enforcement */
+       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
+               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
+               .pwr_restriction = cpu_to_le16(tx_power),
+       };
+
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+                                   sizeof(reduce_txpwr_cmd),
+                                   &reduce_txpwr_cmd);
+}
+
 static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
 {
        return 0;
@@ -766,7 +784,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
-                       iwl_mvm_bt_coex_vif_assoc(mvm, vif);
                        iwl_mvm_configure_mcast_filter(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
@@ -779,9 +796,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
-               ret = iwl_mvm_power_update_mode(mvm, vif);
-               if (ret)
-                       IWL_ERR(mvm, "failed to update power mode\n");
+               if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
+                       /* Workaround for FW bug, otherwise FW disables device
+                        * power save upon disassociation
+                        */
+                       ret = iwl_mvm_power_update_mode(mvm, vif);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update power mode\n");
+               }
+               iwl_mvm_bt_coex_vif_assoc(mvm, vif);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
@@ -794,6 +817,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
+       if (changes & BSS_CHANGED_TXPOWER) {
+               IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+                               bss_conf->txpower);
+               iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+       }
 }
 
 static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
index 420e82d..29bebae 100644 (file)
@@ -76,6 +76,7 @@
 #include "iwl-trans.h"
 #include "sta.h"
 #include "fw-api.h"
+#include "constants.h"
 
 #define IWL_INVALID_MAC80211_QUEUE     0xff
 #define IWL_MVM_MAX_ADDRESSES          5
@@ -91,6 +92,9 @@ enum iwl_mvm_tx_fifo {
 };
 
 extern struct ieee80211_ops iwl_mvm_hw_ops;
+extern const struct iwl_mvm_power_ops pm_legacy_ops;
+extern const struct iwl_mvm_power_ops pm_mac_ops;
+
 /**
  * struct iwl_mvm_mod_params - module parameters for iwlmvm
  * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
@@ -150,6 +154,17 @@ enum iwl_power_scheme {
 
 #define IWL_CONN_MAX_LISTEN_INTERVAL   70
 
+struct iwl_mvm_power_ops {
+       int (*power_update_mode)(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
+       int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               char *buf, int bufsz);
+#endif
+};
+
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0),
@@ -163,7 +178,7 @@ enum iwl_dbgfs_pm_mask {
 };
 
 struct iwl_dbgfs_pm {
-       u8 keep_alive_seconds;
+       u16 keep_alive_seconds;
        u32 rx_data_timeout;
        u32 tx_data_timeout;
        bool skip_over_dtim;
@@ -180,24 +195,28 @@ enum iwl_dbgfs_bf_mask {
        MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
        MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
        MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
-       MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3),
-       MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4),
-       MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5),
-       MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6),
-       MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7),
-       MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8),
+       MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3),
+       MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4),
+       MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5),
+       MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6),
+       MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7),
+       MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8),
+       MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9),
+       MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10),
 };
 
 struct iwl_dbgfs_bf {
-       u8 bf_energy_delta;
-       u8 bf_roaming_energy_delta;
-       u8 bf_roaming_state;
-       u8 bf_temperature_delta;
-       u8 bf_enable_beacon_filter;
-       u8 bf_debug_flag;
+       u32 bf_energy_delta;
+       u32 bf_roaming_energy_delta;
+       u32 bf_roaming_state;
+       u32 bf_temp_threshold;
+       u32 bf_temp_fast_filter;
+       u32 bf_temp_slow_filter;
+       u32 bf_enable_beacon_filter;
+       u32 bf_debug_flag;
        u32 bf_escape_timer;
        u32 ba_escape_timer;
-       u8 ba_enable_beacon_abort;
+       u32 ba_enable_beacon_abort;
        int mask;
 };
 #endif
@@ -268,7 +287,7 @@ struct iwl_mvm_vif {
 
 #if IS_ENABLED(CONFIG_IPV6)
        /* IPv6 addresses for WoWLAN */
-       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
+       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
        int num_target_ipv6_addrs;
 #endif
 #endif
@@ -459,6 +478,9 @@ struct iwl_mvm {
         */
        u8 vif_count;
 
+       /* -1 for always, 0 for never, >0 for that many times */
+       s8 restart_fw;
+
        struct led_classdev led;
 
        struct ieee80211_vif *p2p_device_vif;
@@ -482,6 +504,8 @@ struct iwl_mvm {
        /* Thermal Throttling and CTkill */
        struct iwl_mvm_tt_mgmt thermal_throttle;
        s32 temperature;        /* Celsius */
+
+       const struct iwl_mvm_power_ops *pm_ops;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -525,6 +549,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
                                        enum ieee80211_band band);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
+void iwl_mvm_dump_sram(struct iwl_mvm *mvm);
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
 
@@ -660,10 +685,26 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
                        u8 flags, bool init);
 
 /* power managment */
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                            struct iwl_powertable_cmd *cmd);
+static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif)
+{
+       return mvm->pm_ops->power_update_mode(mvm, vif);
+}
+
+static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif)
+{
+       return mvm->pm_ops->power_disable(mvm, vif);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif,
+                                           char *buf, int bufsz)
+{
+       return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
+}
+#endif
 
 int iwl_mvm_leds_init(struct iwl_mvm *mvm);
 void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@@ -707,6 +748,10 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif);
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+                                  struct iwl_beacon_filter_cmd *cmd);
+int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif, bool enable);
 
 /* SMPS */
 void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index af79a14..65e5fb8 100644 (file)
@@ -275,6 +275,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(BEACON_NOTIFICATION),
        CMD(BEACON_TEMPLATE_CMD),
        CMD(STATISTICS_NOTIFICATION),
+       CMD(REDUCE_TX_POWER_CMD),
        CMD(TX_ANT_CONFIGURATION_CMD),
        CMD(D3_CONFIG_CMD),
        CMD(PROT_OFFLOAD_CONFIG_CMD),
@@ -301,6 +302,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(MCAST_FILTER_CMD),
        CMD(REPLY_BEACON_FILTERING_CMD),
        CMD(REPLY_THERMAL_MNG_BACKOFF),
+       CMD(MAC_PM_POWER_TABLE),
 };
 #undef CMD
 
@@ -340,6 +342,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->fw = fw;
        mvm->hw = hw;
 
+       mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
+
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
@@ -431,6 +435,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_unregister;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)
+               mvm->pm_ops = &pm_mac_ops;
+       else
+               mvm->pm_ops = &pm_legacy_ops;
+
        return op_mode;
 
  out_unregister:
@@ -638,6 +647,22 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
        ieee80211_free_txskb(mvm->hw, skb);
 }
 
+struct iwl_mvm_reprobe {
+       struct device *dev;
+       struct work_struct work;
+};
+
+static void iwl_mvm_reprobe_wk(struct work_struct *wk)
+{
+       struct iwl_mvm_reprobe *reprobe;
+
+       reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
+       if (device_reprobe(reprobe->dev))
+               dev_err(reprobe->dev, "reprobe failed!\n");
+       kfree(reprobe);
+       module_put(THIS_MODULE);
+}
+
 static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
 {
        iwl_abort_notification_waits(&mvm->notif_wait);
@@ -649,9 +674,30 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
         * can't recover this since we're already half suspended.
         */
        if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
-       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
-                  iwlwifi_mod_params.restart_fw) {
+               struct iwl_mvm_reprobe *reprobe;
+
+               IWL_ERR(mvm,
+                       "Firmware error during reconfiguration - reprobe!\n");
+
+               /*
+                * get a module reference to avoid doing this while unloading
+                * anyway and to avoid scheduling a work with code that's
+                * being removed.
+                */
+               if (!try_module_get(THIS_MODULE)) {
+                       IWL_ERR(mvm, "Module is being unloaded - abort\n");
+                       return;
+               }
+
+               reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC);
+               if (!reprobe) {
+                       module_put(THIS_MODULE);
+                       return;
+               }
+               reprobe->dev = mvm->trans->dev;
+               INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
+               schedule_work(&reprobe->work);
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
                /*
                 * This is a bit racy, but worst case we tell mac80211 about
                 * a stopped/aborted (sched) scan when that was already done
@@ -669,6 +715,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                        break;
                }
 
+               if (mvm->restart_fw > 0)
+                       mvm->restart_fw--;
                ieee80211_restart_hw(mvm->hw);
        }
 }
@@ -678,6 +726,8 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
        iwl_mvm_dump_nic_error_log(mvm);
+       if (!mvm->restart_fw)
+               iwl_mvm_dump_sram(mvm);
 
        iwl_mvm_nic_restart(mvm);
 }
index e7ca965..4e7c9f2 100644 (file)
@@ -75,8 +75,8 @@
 
 #define POWER_KEEP_ALIVE_PERIOD_SEC    25
 
-static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
-                                         struct iwl_beacon_filter_cmd *cmd)
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+                                  struct iwl_beacon_filter_cmd *cmd)
 {
        int ret;
 
@@ -85,52 +85,60 @@ static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
 
        if (!ret) {
                IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
-                               cmd->ba_enable_beacon_abort);
+                               le32_to_cpu(cmd->ba_enable_beacon_abort));
                IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
-                               cmd->ba_escape_timer);
+                               le32_to_cpu(cmd->ba_escape_timer));
                IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
-                               cmd->bf_debug_flag);
+                               le32_to_cpu(cmd->bf_debug_flag));
                IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
-                               cmd->bf_enable_beacon_filter);
+                               le32_to_cpu(cmd->bf_enable_beacon_filter));
                IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
-                               cmd->bf_energy_delta);
+                               le32_to_cpu(cmd->bf_energy_delta));
                IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
-                               cmd->bf_escape_timer);
+                               le32_to_cpu(cmd->bf_escape_timer));
                IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
-                               cmd->bf_roaming_energy_delta);
+                               le32_to_cpu(cmd->bf_roaming_energy_delta));
                IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
-                               cmd->bf_roaming_state);
-               IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
-                               cmd->bf_temperature_delta);
+                               le32_to_cpu(cmd->bf_roaming_state));
+               IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_threshold));
+               IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_fast_filter));
+               IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_slow_filter));
        }
        return ret;
 }
 
-static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
-                                      struct ieee80211_vif *vif, bool enable)
+int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif, bool enable)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_beacon_filter_cmd cmd = {
                IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = 1,
-               .ba_enable_beacon_abort = enable,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
+               .ba_enable_beacon_abort = cpu_to_le32(enable),
        };
 
        if (!mvmvif->bf_enabled)
                return 0;
 
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
+               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
        return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 }
 
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
-                             struct iwl_powertable_cmd *cmd)
+                             struct iwl_mac_power_cmd *cmd)
 {
        IWL_DEBUG_POWER(mvm,
-                       "Sending power table command for power level %d, flags = 0x%X\n",
-                       iwlmvm_mod_params.power_scheme,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd->id_and_color, iwlmvm_mod_params.power_scheme,
                        le16_to_cpu(cmd->flags));
-       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
+       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+                       le16_to_cpu(cmd->keep_alive_seconds));
 
        if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
                IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
@@ -139,15 +147,16 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
                                le32_to_cpu(cmd->tx_data_timeout));
                if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
                        IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
-                                       le32_to_cpu(cmd->skip_dtim_periods));
+                                       cmd->skip_dtim_periods);
                if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
                        IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
-                                       le32_to_cpu(cmd->lprx_rssi_threshold));
+                                       cmd->lprx_rssi_threshold);
        }
 }
 
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                            struct iwl_powertable_cmd *cmd)
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct iwl_mac_power_cmd *cmd)
 {
        struct ieee80211_hw *hw = mvm->hw;
        struct ieee80211_chanctx_conf *chanctx_conf;
@@ -158,19 +167,26 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
 
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       dtimper = hw->conf.ps_dtim_period ?: 1;
+
        /*
         * Regardless of power management state the driver must set
         * keep alive period. FW will use it for sending keep alive NDPs
-        * immediately after association.
+        * immediately after association. Check that keep alive period
+        * is at least 3 * DTIM
         */
-       cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+       cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
        if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-       if (!vif->bss_conf.assoc)
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
@@ -186,12 +202,9 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
            (vif->bss_conf.beacon_rate->bitrate == 10 ||
             vif->bss_conf.beacon_rate->bitrate == 60)) {
                cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
+               cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
        }
 
-       dtimper = hw->conf.ps_dtim_period ?: 1;
-
        /* Check if radar detection is required on current channel */
        rcu_read_lock();
        chanctx_conf = rcu_dereference(vif->chanctx_conf);
@@ -207,27 +220,25 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
            (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
             mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
                cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               cmd->skip_dtim_periods = cpu_to_le32(3);
+               cmd->skip_dtim_periods = 3;
        }
 
-       /* Check that keep alive period is at least 3 * DTIM */
-       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-       keep_alive = max_t(int, 3 * dtimper_msec,
-                          MSEC_PER_SEC * cmd->keep_alive_seconds);
-       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
-       cmd->keep_alive_seconds = keep_alive;
-
        if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
-               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+               cmd->rx_data_timeout =
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout =
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
        } else {
-               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+               cmd->rx_data_timeout =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
        }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
-               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+               cmd->keep_alive_seconds =
+                       cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
                if (mvmvif->dbgfs_pm.skip_over_dtim)
                        cmd->flags |=
@@ -243,8 +254,7 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                cmd->tx_data_timeout =
                        cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
-               cmd->skip_dtim_periods =
-                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+               cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
                if (mvmvif->dbgfs_pm.lprx_ena)
                        cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
@@ -252,16 +262,16 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                        cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
        }
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
+               cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif)
 {
        int ret;
        bool ba_enable;
-       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mac_power_cmd cmd = {};
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
@@ -280,7 +290,7 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
                                   sizeof(cmd), &cmd);
        if (ret)
                return ret;
@@ -291,15 +301,19 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
 }
 
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif)
 {
-       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mac_power_cmd cmd = {};
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                          mvmvif->color));
+
        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
                cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
@@ -310,11 +324,50 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 #endif
        iwl_mvm_power_log(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
                                    sizeof(cmd), &cmd);
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif, char *buf,
+                                       int bufsz)
+{
+       struct iwl_mac_power_cmd cmd = {};
+       int pos = 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                        0 : 1);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        cmd.skip_dtim_periods);
+       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+                        iwlmvm_mod_params.power_scheme);
+       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+                        le16_to_cpu(cmd.flags));
+       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+                        le16_to_cpu(cmd.keep_alive_seconds));
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+                                1 : 0);
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "lprx_rssi_threshold = %d\n",
+                                        cmd.lprx_rssi_threshold);
+       }
+       return pos;
+}
+
 void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd)
@@ -323,22 +376,30 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
        struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
 
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
-               cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
+               cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
                cmd->bf_roaming_energy_delta =
-                                dbgfs_bf->bf_roaming_energy_delta;
+                               cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
-               cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
-       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
-               cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
+               cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
+               cmd->bf_temp_threshold =
+                               cpu_to_le32(dbgfs_bf->bf_temp_threshold);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
+               cmd->bf_temp_fast_filter =
+                               cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
+               cmd->bf_temp_slow_filter =
+                               cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
-               cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
+               cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
                cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
                cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
-               cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
+               cmd->ba_enable_beacon_abort =
+                               cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
 }
 #endif
 
@@ -348,7 +409,7 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_beacon_filter_cmd cmd = {
                IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = 1,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
        };
        int ret;
 
@@ -372,7 +433,8 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
+           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
@@ -382,3 +444,11 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
 
        return ret;
 }
+
+const struct iwl_mvm_power_ops pm_mac_ops = {
+       .power_update_mode = iwl_mvm_power_mac_update_mode,
+       .power_disable = iwl_mvm_power_mac_disable,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+#endif
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
new file mode 100644 (file)
index 0000000..2ce79ba
--- /dev/null
@@ -0,0 +1,319 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "mvm.h"
+#include "iwl-modparams.h"
+#include "fw-api-power.h"
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC    25
+
+static void iwl_mvm_power_log(struct iwl_mvm *mvm,
+                             struct iwl_powertable_cmd *cmd)
+{
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command for power level %d, flags = 0x%X\n",
+                       iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd->flags));
+       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
+
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+                               le32_to_cpu(cmd->rx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd->tx_data_timeout));
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+                       IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+                                       le32_to_cpu(cmd->skip_dtim_periods));
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                                       le32_to_cpu(cmd->lprx_rssi_threshold));
+       }
+}
+
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct iwl_powertable_cmd *cmd)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *chan;
+       int dtimper, dtimper_msec;
+       int keep_alive;
+       bool radar_detect = false;
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
+
+       /*
+        * Regardless of power management state the driver must set
+        * keep alive period. FW will use it for sending keep alive NDPs
+        * immediately after association.
+        */
+       cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       if (!vif->bss_conf.assoc)
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       if (!vif->bss_conf.ps)
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+       if (vif->bss_conf.beacon_rate &&
+           (vif->bss_conf.beacon_rate->bitrate == 10 ||
+            vif->bss_conf.beacon_rate->bitrate == 60)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
+       }
+
+       dtimper = hw->conf.ps_dtim_period ?: 1;
+
+       /* Check if radar detection is required on current channel */
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       WARN_ON(!chanctx_conf);
+       if (chanctx_conf) {
+               chan = chanctx_conf->def.chan;
+               radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
+       }
+       rcu_read_unlock();
+
+       /* Check skip over DTIM conditions */
+       if (!radar_detect && (dtimper <= 10) &&
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
+            mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               cmd->skip_dtim_periods = cpu_to_le32(3);
+       }
+
+       /* Check that keep alive period is at least 3 * DTIM */
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * cmd->keep_alive_seconds);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+       cmd->keep_alive_seconds = keep_alive;
+
+       if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
+               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+       } else {
+               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+       }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
+               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
+               if (mvmvif->dbgfs_pm.skip_over_dtim)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               else
+                       cmd->flags &=
+                               cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
+               cmd->rx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
+               cmd->tx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
+               cmd->skip_dtim_periods =
+                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
+               if (mvmvif->dbgfs_pm.lprx_ena)
+                       cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               else
+                       cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+}
+
+static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif)
+{
+       int ret;
+       bool ba_enable;
+       struct iwl_powertable_cmd cmd = {};
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       /*
+        * TODO: The following vif_count verification is temporary condition.
+        * Avoid power mode update if more than one interface is currently
+        * active. Remove this condition when FW will support power management
+        * on multiple MACs.
+        */
+       IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
+                       mvm->vif_count);
+       if (mvm->vif_count > 1)
+               return 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       iwl_mvm_power_log(mvm, &cmd);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               return ret;
+
+       ba_enable = !!(cmd.flags &
+                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
+
+       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
+}
+
+static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+               cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       iwl_mvm_power_log(mvm, &cmd);
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif, char *buf,
+                                          int bufsz)
+{
+       struct iwl_powertable_cmd cmd = {};
+       int pos = 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                        0 : 1);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        le32_to_cpu(cmd.skip_dtim_periods));
+       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+                        iwlmvm_mod_params.power_scheme);
+       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+                        le16_to_cpu(cmd.flags));
+       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+                        cmd.keep_alive_seconds);
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+                                1 : 0);
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "lprx_rssi_threshold = %d\n",
+                                        le32_to_cpu(cmd.lprx_rssi_threshold));
+       }
+       return pos;
+}
+#endif
+
+const struct iwl_mvm_power_ops pm_legacy_ops = {
+       .power_update_mode = iwl_mvm_power_legacy_update_mode,
+       .power_disable = iwl_mvm_power_legacy_disable,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
+#endif
+};
index 29d49cf..1897387 100644 (file)
@@ -132,7 +132,7 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
        struct iwl_time_quota_cmd cmd;
-       int i, idx, ret, num_active_bindings, quota, quota_rem;
+       int i, idx, ret, num_active_macs, quota, quota_rem;
        struct iwl_mvm_quota_iterator_data data = {
                .n_interfaces = {},
                .colors = { -1, -1, -1, -1 },
@@ -162,18 +162,17 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
         * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
         * equally between all the bindings that require quota
         */
-       num_active_bindings = 0;
+       num_active_macs = 0;
        for (i = 0; i < MAX_BINDINGS; i++) {
                cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
-               if (data.n_interfaces[i] > 0)
-                       num_active_bindings++;
+               num_active_macs += data.n_interfaces[i];
        }
 
        quota = 0;
        quota_rem = 0;
-       if (num_active_bindings) {
-               quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
-               quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+       if (num_active_macs) {
+               quota = IWL_MVM_MAX_QUOTA / num_active_macs;
+               quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
        }
 
        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@@ -187,7 +186,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                        cmd.quotas[idx].quota = cpu_to_le32(0);
                        cmd.quotas[idx].max_duration = cpu_to_le32(0);
                } else {
-                       cmd.quotas[idx].quota = cpu_to_le32(quota);
+                       cmd.quotas[idx].quota =
+                               cpu_to_le32(quota * data.n_interfaces[i]);
                        cmd.quotas[idx].max_duration =
                                cpu_to_le32(IWL_MVM_MAX_QUOTA);
                }
index b328a98..9eeb21f 100644 (file)
@@ -2688,9 +2688,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        lq_sta->flush_timer = 0;
        lq_sta->supp_rates = sta->supp_rates[sband->band];
-       for (j = 0; j < LQ_SIZE; j++)
-               for (i = 0; i < IWL_RATE_COUNT; i++)
-                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        IWL_DEBUG_RATE(mvm,
                       "LQ: *** rate scale station global init for station %d ***\n",
@@ -3159,8 +3156,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *mvm_r,
-                                struct ieee80211_supported_band *sband,
-                                struct ieee80211_sta *sta, void *mvm_sta)
+                             struct ieee80211_supported_band *sband,
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *mvm_sta)
 {
 }
 static struct rate_control_ops rs_mvm_ops = {
@@ -3193,13 +3191,14 @@ void iwl_mvm_rate_control_unregister(void)
  * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
  * Tx protection, according to this rquest and previous requests,
  * and send the LQ command.
- * @lq: The LQ command
  * @mvmsta: The station
  * @enable: Enable Tx protection?
  */
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
-                         struct iwl_mvm_sta *mvmsta, bool enable)
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                         bool enable)
 {
+       struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
+
        lockdep_assert_held(&mvm->mutex);
 
        if (enable) {
index cff4f6d..29d699a 100644 (file)
@@ -404,7 +404,7 @@ extern void iwl_mvm_rate_control_unregister(void);
 
 struct iwl_mvm_sta;
 
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
-                         struct iwl_mvm_sta *mvmsta, bool enable);
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                         bool enable);
 
 #endif /* __rs__ */
index e4930d5..6fd7fae 100644 (file)
@@ -124,24 +124,15 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
        ieee80211_rx_ni(mvm->hw, skb);
 }
 
-/*
- * iwl_mvm_calc_rssi - calculate the rssi in dBm
- * @phy_info: the phy information for the coming packet
- */
-static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
-                            struct iwl_rx_phy_info *phy_info)
+static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
+                             struct iwl_rx_phy_info *phy_info,
+                             struct ieee80211_rx_status *rx_status)
 {
        int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
        int rssi_all_band_a, rssi_all_band_b;
        u32 agc_a, agc_b, max_agc;
        u32 val;
 
-       /* Find max rssi among 2 possible receivers.
-        * These values are measured by the Digital Signal Processor (DSP).
-        * They should stay fairly constant even as the signal strength varies,
-        * if the radio's Automatic Gain Control (AGC) is working right.
-        * AGC value (see below) will provide the "interesting" info.
-        */
        val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
        agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
        agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
@@ -166,7 +157,45 @@ static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
        IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
                        rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
 
-       return max_rssi_dbm;
+       rx_status->signal = max_rssi_dbm;
+       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+       rx_status->chain_signal[0] = rssi_a_dbm;
+       rx_status->chain_signal[1] = rssi_b_dbm;
+}
+
+/*
+ * iwl_mvm_get_signal_strength - use new rx PHY INFO API
+ */
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+                                       struct iwl_rx_phy_info *phy_info,
+                                       struct ieee80211_rx_status *rx_status)
+{
+       int energy_a, energy_b, energy_c, max_energy;
+       u32 val;
+
+       val =
+           le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
+       energy_a = -((val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_A_POS);
+       energy_b = -((val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_B_POS);
+       energy_c = -((val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_C_POS);
+       max_energy = max(energy_a, energy_b);
+       max_energy = max(max_energy, energy_c);
+
+       IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+                       energy_a, energy_b, energy_c, max_energy);
+
+       rx_status->signal = max_energy;
+       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+       rx_status->chain_signal[0] = energy_a;
+       rx_status->chain_signal[1] = energy_b;
+       rx_status->chain_signal[2] = energy_c;
 }
 
 /*
@@ -289,29 +318,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
         */
        /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
 
-       /* Find max signal strength (dBm) among 3 antenna/receiver chains */
-       rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API)
+               iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
+       else
+               iwl_mvm_calc_rssi(mvm, phy_info, &rx_status);
 
        IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
                              (unsigned long long)rx_status.mactime);
 
-       /*
-        * "antenna number"
-        *
-        * It seems that the antenna field in the phy flags value
-        * is actually a bit field. This is undefined by radiotap,
-        * it wants an actual antenna number but I always get "7"
-        * for most legacy frames I receive indicating that the
-        * same frame was received on all three RX chains.
-        *
-        * I think this field should be removed in favor of a
-        * new 802.11n radiotap field "RX chains" that is defined
-        * as a bitmask.
-        */
-       rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
-                               RX_RES_PHY_FLAGS_ANTENNA)
-                               >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-
        /* set the preamble flag if appropriate */
        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
                rx_status.flag |= RX_FLAG_SHORTPRE;
index acdff6b..9a7ab84 100644 (file)
@@ -301,10 +301,12 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
         */
        if (req->n_ssids > 0) {
                cmd->passive2active = cpu_to_le16(1);
+               cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
                ssid = req->ssids[0].ssid;
                ssid_len = req->ssids[0].ssid_len;
        } else {
                cmd->passive2active = 0;
+               cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
        iwl_mvm_scan_fill_ssids(cmd, req);
index 563f559..44add29 100644 (file)
@@ -826,8 +826,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                 * method for HT traffic
                 * this function also sends the LQ command
                 */
-               return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
-                                            mvmsta, true);
+               return iwl_mvm_tx_protection(mvm, mvmsta, true);
                /*
                 * TODO: remove the TLC_RTS flag when we tear down the last
                 * AGG session (agg_tids_count in DVM)
index d6ae7f1..1f3282d 100644 (file)
@@ -391,8 +391,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
                mvmsta = (void *)sta->drv_priv;
                if (enable == mvmsta->tt_tx_protection)
                        continue;
-               err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
-                                           mvmsta, enable);
+               err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
                if (err) {
                        IWL_ERR(mvm, "Failed to %s Tx protection\n",
                                enable ? "enable" : "disable");
@@ -513,12 +512,39 @@ static const struct iwl_tt_params iwl7000_tt_params = {
        .support_tx_backoff = true,
 };
 
+static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
+       .ct_kill_entry = 118,
+       .ct_kill_exit = 96,
+       .ct_kill_duration = 5,
+       .dynamic_smps_entry = 114,
+       .dynamic_smps_exit = 110,
+       .tx_protection_entry = 114,
+       .tx_protection_exit = 108,
+       .tx_backoff = {
+               {.temperature = 112, .backoff = 300},
+               {.temperature = 113, .backoff = 800},
+               {.temperature = 114, .backoff = 1500},
+               {.temperature = 115, .backoff = 3000},
+               {.temperature = 116, .backoff = 5000},
+               {.temperature = 117, .backoff = 10000},
+       },
+       .support_ct_kill = true,
+       .support_dynamic_smps = true,
+       .support_tx_protection = true,
+       .support_tx_backoff = true,
+};
+
 void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
 {
        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 
        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
-       tt->params = &iwl7000_tt_params;
+
+       if (mvm->cfg->high_temp)
+               tt->params = &iwl7000_high_temp_tt_params;
+       else
+               tt->params = &iwl7000_tt_params;
+
        tt->throttle = false;
        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 }
index f0e96a9..7694b8f 100644 (file)
@@ -123,6 +123,8 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                 * it
                 */
                WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+       } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+               tx_cmd->pm_frame_timeout = cpu_to_le16(2);
        } else {
                tx_cmd->pm_frame_timeout = 0;
        }
index 1e13328..a9c3574 100644 (file)
@@ -453,6 +453,29 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
        IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
 }
 
+void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
+{
+       const struct fw_img *img;
+       int ofs, len = 0;
+       u8 *buf;
+
+       if (!mvm->ucode_loaded)
+               return;
+
+       img = &mvm->fw->img[mvm->cur_ucode];
+       ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       buf = kzalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
+       iwl_print_hex_error(mvm->trans, buf, len);
+
+       kfree(buf);
+}
+
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
index ff13458..c96070f 100644 (file)
@@ -273,9 +273,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
index b654dcd..fa22639 100644 (file)
@@ -392,7 +392,6 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 /*****************************************************
 * Error handling
 ******************************************************/
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf);
 void iwl_pcie_dump_csr(struct iwl_trans *trans);
 
 /*****************************************************
index f600e68..68837d4 100644 (file)
@@ -793,7 +793,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
        }
 
        iwl_pcie_dump_csr(trans);
-       iwl_pcie_dump_fh(trans, NULL);
+       iwl_dump_fh(trans, NULL);
 
        set_bit(STATUS_FW_ERROR, &trans_pcie->status);
        clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
index 96cfcdd..805e303 100644 (file)
@@ -1038,71 +1038,6 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 }
 
-static const char *get_fh_string(int cmd)
-{
-#define IWL_CMD(x) case x: return #x
-       switch (cmd) {
-       IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
-       IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
-       IWL_CMD(FH_RSCSR_CHNL0_WPTR);
-       IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
-       IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
-       IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
-       IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
-       IWL_CMD(FH_TSSR_TX_STATUS_REG);
-       IWL_CMD(FH_TSSR_TX_ERROR_REG);
-       default:
-               return "UNKNOWN";
-       }
-#undef IWL_CMD
-}
-
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf)
-{
-       int i;
-       static const u32 fh_tbl[] = {
-               FH_RSCSR_CHNL0_STTS_WPTR_REG,
-               FH_RSCSR_CHNL0_RBDCB_BASE_REG,
-               FH_RSCSR_CHNL0_WPTR,
-               FH_MEM_RCSR_CHNL0_CONFIG_REG,
-               FH_MEM_RSSR_SHARED_CTRL_REG,
-               FH_MEM_RSSR_RX_STATUS_REG,
-               FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
-               FH_TSSR_TX_STATUS_REG,
-               FH_TSSR_TX_ERROR_REG
-       };
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (buf) {
-               int pos = 0;
-               size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
-
-               *buf = kmalloc(bufsz, GFP_KERNEL);
-               if (!*buf)
-                       return -ENOMEM;
-
-               pos += scnprintf(*buf + pos, bufsz - pos,
-                               "FH register values:\n");
-
-               for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
-                       pos += scnprintf(*buf + pos, bufsz - pos,
-                               "  %34s: 0X%08x\n",
-                               get_fh_string(fh_tbl[i]),
-                               iwl_read_direct32(trans, fh_tbl[i]));
-
-               return pos;
-       }
-#endif
-
-       IWL_ERR(trans, "FH register values:\n");
-       for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
-               IWL_ERR(trans, "  %34s: 0X%08x\n",
-                       get_fh_string(fh_tbl[i]),
-                       iwl_read_direct32(trans, fh_tbl[i]));
-
-       return 0;
-}
-
 static const char *get_csr_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -1183,18 +1118,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans)
 } while (0)
 
 /* file operation */
-#define DEBUGFS_READ_FUNC(name)                                         \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file,               \
-                                       char __user *user_buf,          \
-                                       size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name)                                        \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file,              \
-                                       const char __user *user_buf,    \
-                                       size_t count, loff_t *ppos);
-
 #define DEBUGFS_READ_FILE_OPS(name)                                    \
-       DEBUGFS_READ_FUNC(name);                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = {         \
        .read = iwl_dbgfs_##name##_read,                                \
        .open = simple_open,                                            \
@@ -1202,7 +1126,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {            \
 };
 
 #define DEBUGFS_WRITE_FILE_OPS(name)                                    \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .open = simple_open,                                            \
@@ -1210,8 +1133,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 };
 
 #define DEBUGFS_READ_WRITE_FILE_OPS(name)                              \
-       DEBUGFS_READ_FUNC(name);                                        \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {         \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
@@ -1395,7 +1316,7 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
        int pos = 0;
        ssize_t ret = -EFAULT;
 
-       ret = pos = iwl_pcie_dump_fh(trans, &buf);
+       ret = pos = iwl_dump_fh(trans, &buf);
        if (buf) {
                ret = simple_read_from_buffer(user_buf,
                                              count, ppos, buf, pos);
@@ -1502,10 +1423,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
-       /* W/A - seems to solve weird behavior. We need to remove this if we
-        * don't want to stay in L1 all the time. This wastes a lot of power */
-       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
-                              PCIE_LINK_STATE_CLKPM);
+       if (!cfg->base_params->pcie_l1_allowed) {
+               /*
+                * W/A - seems to solve weird behavior. We need to remove this
+                * if we don't want to stay in L1 all the time. This wastes a
+                * lot of power.
+                */
+               pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+                                      PCIE_LINK_STATE_L1 |
+                                      PCIE_LINK_STATE_CLKPM);
+       }
 
        if (pci_enable_device(pdev)) {
                err = -ENODEV;
index c47c921..134f7a1 100644 (file)
@@ -1619,10 +1619,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
 
-       if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
-               WARN_ON_ONCE(1);
+       if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used),
+                     "TX on unused queue %d\n", txq_id))
                return -EINVAL;
-       }
 
        spin_lock(&txq->lock);
 
index cb34c78..7b2a622 100644 (file)
@@ -867,7 +867,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
 
        if (WARN_ON(skb->len < 10)) {
                /* Should not happen; just a sanity check for addr1 use */
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -884,13 +884,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        }
 
        if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
        if (data->idle && !data->tmp_chan) {
                wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -2309,7 +2309,9 @@ static int __init init_mac80211_hwsim(void)
                        hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
 
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
-                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                                   WIPHY_FLAG_AP_UAPSD;
+               hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
                /* ask mac80211 to reserve space for magic */
                hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index a78e065..8f9f542 100644 (file)
@@ -189,7 +189,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
                skb_src = skb_dequeue(&pra_list->skb_head);
 
-               pra_list->total_pkts_size -= skb_src->len;
+               pra_list->total_pkt_count--;
 
                atomic_dec(&priv->wmm.tx_pkts_queued);
 
@@ -268,7 +268,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
                skb_queue_tail(&pra_list->skb_head, skb_aggr);
 
-               pra_list->total_pkts_size += skb_aggr->len;
+               pra_list->total_pkt_count++;
 
                atomic_inc(&priv->wmm.tx_pkts_queued);
 
index 89459db..c5e21ed 100644 (file)
@@ -25,7 +25,9 @@ module_param(reg_alpha2, charp, 0);
 
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
-               .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
+               .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
+                                  BIT(NL80211_IFTYPE_P2P_GO) |
+                                  BIT(NL80211_IFTYPE_P2P_CLIENT),
        },
        {
                .max = 1, .types = BIT(NL80211_IFTYPE_AP),
@@ -189,6 +191,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct sk_buff *skb;
        u16 pkt_len;
        const struct ieee80211_mgmt *mgmt;
+       struct mwifiex_txinfo *tx_info;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
 
        if (!buf || !len) {
@@ -216,6 +219,10 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -ENOMEM;
        }
 
+       tx_info = MWIFIEX_SKB_TXCB(skb);
+       tx_info->bss_num = priv->bss_num;
+       tx_info->bss_type = priv->bss_type;
+
        mwifiex_form_mgmt_frame(skb, buf, len);
        mwifiex_queue_tx_pkt(priv, skb);
 
@@ -235,16 +242,20 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
                                     u16 frame_type, bool reg)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+       u32 mask;
 
        if (reg)
-               priv->mgmt_frame_mask |= BIT(frame_type >> 4);
+               mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
        else
-               priv->mgmt_frame_mask &= ~BIT(frame_type >> 4);
-
-       mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
-                              HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask);
+               mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
 
-       wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       if (mask != priv->mgmt_frame_mask) {
+               priv->mgmt_frame_mask = mask;
+               mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
+                                      HostCmd_ACT_GEN_SET, 0,
+                                      &priv->mgmt_frame_mask);
+               wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       }
 }
 
 /*
@@ -2298,8 +2309,7 @@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
 
 #ifdef CONFIG_PM
 static bool
-mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
-                            s8 *byte_seq)
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq)
 {
        int j, k, valid_byte_cnt = 0;
        bool dont_care_byte = false;
index 5178c46..9eefacb 100644 (file)
@@ -404,11 +404,43 @@ mwifiex_is_rate_auto(struct mwifiex_private *priv)
                return false;
 }
 
-/*
- * This function gets the supported data rates.
- *
- * The function works in both Ad-Hoc and infra mode by printing the
- * band and returning the data rates.
+/* This function gets the supported data rates from bitmask inside
+ * cfg80211_scan_request.
+ */
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+                                   u8 *rates, u8 radio_type)
+{
+       struct wiphy *wiphy = priv->adapter->wiphy;
+       struct cfg80211_scan_request *request = priv->scan_request;
+       u32 num_rates, rate_mask;
+       struct ieee80211_supported_band *sband;
+       int i;
+
+       if (radio_type) {
+               sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+               if (WARN_ON_ONCE(!sband))
+                       return 0;
+               rate_mask = request->rates[IEEE80211_BAND_5GHZ];
+       } else {
+               sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+               if (WARN_ON_ONCE(!sband))
+                       return 0;
+               rate_mask = request->rates[IEEE80211_BAND_2GHZ];
+       }
+
+       num_rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((BIT(i) & rate_mask) == 0)
+                       continue; /* skip rate */
+               rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
+       }
+
+       return num_rates;
+}
+
+/* This function gets the supported data rates. The function works in
+ * both Ad-Hoc and infra mode by printing the band and returning the
+ * data rates.
  */
 u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
 {
index 94cc09d..a599347 100644 (file)
@@ -75,7 +75,8 @@
 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
 #define MWIFIEX_BUF_FLAG_BRIDGED_PKT      BIT(1)
 
-#define MWIFIEX_BRIDGED_PKTS_THRESHOLD     1024
+#define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
+#define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
 
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
index 1b45aa5..c0dfc4d 100644 (file)
@@ -85,9 +85,6 @@ enum KEY_TYPE_ID {
 #define WAPI_KEY_LEN                   50
 
 #define MAX_POLL_TRIES                 100
-
-#define MAX_MULTI_INTERFACE_POLL_TRIES  1000
-
 #define MAX_FIRMWARE_POLL_TRIES                        100
 
 #define FIRMWARE_READY_SDIO                            0xfedc
@@ -1369,11 +1366,6 @@ struct host_cmd_ds_802_11_eeprom_access {
        u8 value;
 } __packed;
 
-struct host_cmd_tlv {
-       __le16 type;
-       __le16 len;
-} __packed;
-
 struct mwifiex_assoc_event {
        u8 sta_addr[ETH_ALEN];
        __le16 type;
@@ -1399,99 +1391,99 @@ struct host_cmd_11ac_vht_cfg {
 } __packed;
 
 struct host_cmd_tlv_akmp {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 key_mgmt;
        __le16 key_mgmt_operation;
 } __packed;
 
 struct host_cmd_tlv_pwk_cipher {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 proto;
        u8 cipher;
        u8 reserved;
 } __packed;
 
 struct host_cmd_tlv_gwk_cipher {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 cipher;
        u8 reserved;
 } __packed;
 
 struct host_cmd_tlv_passphrase {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 passphrase[0];
 } __packed;
 
 struct host_cmd_tlv_wep_key {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 key_index;
        u8 is_default;
        u8 key[1];
 };
 
 struct host_cmd_tlv_auth_type {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 auth_type;
 } __packed;
 
 struct host_cmd_tlv_encrypt_protocol {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 proto;
 } __packed;
 
 struct host_cmd_tlv_ssid {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 ssid[0];
 } __packed;
 
 struct host_cmd_tlv_rates {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 rates[0];
 } __packed;
 
 struct host_cmd_tlv_bcast_ssid {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 bcast_ctl;
 } __packed;
 
 struct host_cmd_tlv_beacon_period {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 period;
 } __packed;
 
 struct host_cmd_tlv_dtim_period {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 period;
 } __packed;
 
 struct host_cmd_tlv_frag_threshold {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 frag_thr;
 } __packed;
 
 struct host_cmd_tlv_rts_threshold {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 rts_thr;
 } __packed;
 
 struct host_cmd_tlv_retry_limit {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 limit;
 } __packed;
 
 struct host_cmd_tlv_mac_addr {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 mac_addr[ETH_ALEN];
 } __packed;
 
 struct host_cmd_tlv_channel_band {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 band_config;
        u8 channel;
 } __packed;
 
 struct host_cmd_tlv_ageout_timer {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le32 sta_ao_timer;
 } __packed;
 
index e38342f..220af4f 100644 (file)
@@ -87,7 +87,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
        u8 *tmp;
 
        input_len = le16_to_cpu(ie_list->len);
-       travel_len = sizeof(struct host_cmd_tlv);
+       travel_len = sizeof(struct mwifiex_ie_types_header);
 
        ie_list->len = 0;
 
index 2cf8b96..e021a58 100644 (file)
@@ -135,6 +135,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
 
        priv->csa_chan = 0;
        priv->csa_expire_time = 0;
+       priv->del_list_idx = 0;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
@@ -377,18 +378,11 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
 static void
 mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
 {
-       int i;
-
        if (!adapter) {
                pr_err("%s: adapter is NULL\n", __func__);
                return;
        }
 
-       for (i = 0; i < adapter->priv_num; i++) {
-               if (adapter->priv[i])
-                       del_timer_sync(&adapter->priv[i]->scan_delay_timer);
-       }
-
        mwifiex_cancel_all_pending_cmd(adapter);
 
        /* Free lock variables */
@@ -398,13 +392,8 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
        dev_dbg(adapter->dev, "info: free cmd buffer\n");
        mwifiex_free_cmd_buffer(adapter);
 
-       del_timer(&adapter->cmd_timer);
-
        dev_dbg(adapter->dev, "info: free scan table\n");
 
-       if (adapter->if_ops.cleanup_if)
-               adapter->if_ops.cleanup_if(adapter);
-
        if (adapter->sleep_cfm)
                dev_kfree_skb_any(adapter->sleep_cfm);
 }
@@ -702,7 +691,6 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
                if (!adapter->winner) {
                        dev_notice(adapter->dev,
                                   "FW already running! Skip FW dnld\n");
-                       poll_num = MAX_MULTI_INTERFACE_POLL_TRIES;
                        goto poll_fw;
                }
        }
index 12e7781..9d7c0e6 100644 (file)
@@ -1427,6 +1427,7 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
 
        switch (priv->bss_mode) {
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                return mwifiex_deauthenticate_infra(priv, mac);
        case NL80211_IFTYPE_ADHOC:
                return mwifiex_send_cmd_sync(priv,
index 1753431..3402bff 100644 (file)
@@ -191,12 +191,16 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter)
 {
        s32 i;
 
+       if (adapter->if_ops.cleanup_if)
+               adapter->if_ops.cleanup_if(adapter);
+
        del_timer(&adapter->cmd_timer);
 
        /* Free private structures */
        for (i = 0; i < adapter->priv_num; i++) {
                if (adapter->priv[i]) {
                        mwifiex_free_curr_bcn(adapter->priv[i]);
+                       del_timer_sync(&adapter->priv[i]->scan_delay_timer);
                        kfree(adapter->priv[i]);
                }
        }
@@ -386,6 +390,17 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
 }
 
 /*
+ * This function cancels all works in the queue and destroys
+ * the main workqueue.
+ */
+static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
+{
+       flush_workqueue(adapter->workqueue);
+       destroy_workqueue(adapter->workqueue);
+       adapter->workqueue = NULL;
+}
+
+/*
  * This function gets firmware and initializes it.
  *
  * The main initialization steps followed are -
@@ -394,16 +409,18 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
  */
 static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
 {
-       int ret;
+       int ret, i;
        char fmt[64];
        struct mwifiex_private *priv;
        struct mwifiex_adapter *adapter = context;
        struct mwifiex_fw_image fw;
+       struct semaphore *sem = adapter->card_sem;
+       bool init_failed = false;
 
        if (!firmware) {
                dev_err(adapter->dev,
                        "Failed to get firmware %s\n", adapter->fw_name);
-               goto done;
+               goto err_dnld_fw;
        }
 
        memset(&fw, 0, sizeof(struct mwifiex_fw_image));
@@ -416,7 +433,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        else
                ret = mwifiex_dnld_fw(adapter, &fw);
        if (ret == -1)
-               goto done;
+               goto err_dnld_fw;
 
        dev_notice(adapter->dev, "WLAN FW is active\n");
 
@@ -428,13 +445,15 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        }
 
        /* enable host interrupt after fw dnld is successful */
-       if (adapter->if_ops.enable_int)
-               adapter->if_ops.enable_int(adapter);
+       if (adapter->if_ops.enable_int) {
+               if (adapter->if_ops.enable_int(adapter))
+                       goto err_dnld_fw;
+       }
 
        adapter->init_wait_q_woken = false;
        ret = mwifiex_init_fw(adapter);
        if (ret == -1) {
-               goto done;
+               goto err_init_fw;
        } else if (!ret) {
                adapter->hw_status = MWIFIEX_HW_STATUS_READY;
                goto done;
@@ -443,12 +462,12 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        wait_event_interruptible(adapter->init_wait_q,
                                 adapter->init_wait_q_woken);
        if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
-               goto done;
+               goto err_init_fw;
 
        priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
        if (mwifiex_register_cfg80211(adapter)) {
                dev_err(adapter->dev, "cannot register with cfg80211\n");
-               goto err_init_fw;
+               goto err_register_cfg80211;
        }
 
        rtnl_lock();
@@ -479,20 +498,52 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        goto done;
 
 err_add_intf:
-       mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+       for (i = 0; i < adapter->priv_num; i++) {
+               priv = adapter->priv[i];
+
+               if (!priv)
+                       continue;
+
+               if (priv->wdev && priv->netdev)
+                       mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+       }
        rtnl_unlock();
+err_register_cfg80211:
+       wiphy_unregister(adapter->wiphy);
+       wiphy_free(adapter->wiphy);
 err_init_fw:
        if (adapter->if_ops.disable_int)
                adapter->if_ops.disable_int(adapter);
+err_dnld_fw:
        pr_debug("info: %s: unregister device\n", __func__);
-       adapter->if_ops.unregister_dev(adapter);
+       if (adapter->if_ops.unregister_dev)
+               adapter->if_ops.unregister_dev(adapter);
+
+       if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
+           (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
+               pr_debug("info: %s: shutdown mwifiex\n", __func__);
+               adapter->init_wait_q_woken = false;
+
+               if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+                       wait_event_interruptible(adapter->init_wait_q,
+                                                adapter->init_wait_q_woken);
+       }
+       adapter->surprise_removed = true;
+       mwifiex_terminate_workqueue(adapter);
+       init_failed = true;
 done:
        if (adapter->cal_data) {
                release_firmware(adapter->cal_data);
                adapter->cal_data = NULL;
        }
-       release_firmware(adapter->firmware);
+       if (adapter->firmware) {
+               release_firmware(adapter->firmware);
+               adapter->firmware = NULL;
+       }
        complete(&adapter->fw_load);
+       if (init_failed)
+               mwifiex_free_adapter(adapter);
+       up(sem);
        return;
 }
 
@@ -803,18 +854,6 @@ static void mwifiex_main_work_queue(struct work_struct *work)
 }
 
 /*
- * This function cancels all works in the queue and destroys
- * the main workqueue.
- */
-static void
-mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
-{
-       flush_workqueue(adapter->workqueue);
-       destroy_workqueue(adapter->workqueue);
-       adapter->workqueue = NULL;
-}
-
-/*
  * This function adds the card.
  *
  * This function follows the following major steps to set up the device -
@@ -842,6 +881,7 @@ mwifiex_add_card(void *card, struct semaphore *sem,
        }
 
        adapter->iface_type = iface_type;
+       adapter->card_sem = sem;
 
        adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
        adapter->surprise_removed = false;
@@ -872,17 +912,12 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                goto err_init_fw;
        }
 
-       up(sem);
        return 0;
 
 err_init_fw:
        pr_debug("info: %s: unregister device\n", __func__);
        if (adapter->if_ops.unregister_dev)
                adapter->if_ops.unregister_dev(adapter);
-err_registerdev:
-       adapter->surprise_removed = true;
-       mwifiex_terminate_workqueue(adapter);
-err_kmalloc:
        if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
            (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
                pr_debug("info: %s: shutdown mwifiex\n", __func__);
@@ -892,7 +927,10 @@ err_kmalloc:
                        wait_event_interruptible(adapter->init_wait_q,
                                                 adapter->init_wait_q_woken);
        }
-
+err_registerdev:
+       adapter->surprise_removed = true;
+       mwifiex_terminate_workqueue(adapter);
+err_kmalloc:
        mwifiex_free_adapter(adapter);
 
 err_init_sw:
index 253e0bd..d2e5ccd 100644 (file)
@@ -204,11 +204,11 @@ struct mwifiex_ra_list_tbl {
        struct list_head list;
        struct sk_buff_head skb_head;
        u8 ra[ETH_ALEN];
-       u32 total_pkts_size;
        u32 is_11n_enabled;
        u16 max_amsdu;
-       u16 pkt_count;
+       u16 ba_pkt_count;
        u8 ba_packet_thr;
+       u16 total_pkt_count;
 };
 
 struct mwifiex_tid_tbl {
@@ -515,6 +515,7 @@ struct mwifiex_private {
        bool scan_aborting;
        u8 csa_chan;
        unsigned long csa_expire_time;
+       u8 del_list_idx;
 };
 
 enum mwifiex_ba_status {
@@ -748,6 +749,7 @@ struct mwifiex_adapter {
 
        atomic_t is_tx_received;
        atomic_t pending_bridged_pkts;
+       struct semaphore *card_sem;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -900,6 +902,8 @@ int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask,
 u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv,
                                    u8 *rates);
 u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates);
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+                                   u8 *rates, u8 radio_type);
 u8 mwifiex_is_rate_auto(struct mwifiex_private *priv);
 extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE];
 void mwifiex_save_curr_bcn(struct mwifiex_private *priv);
index 20c9c4c..52da8ee 100644 (file)
@@ -76,7 +76,7 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
        return false;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /*
  * Kernel needs to suspend all functions separately. Therefore all
  * registered functions must have drivers with suspend and resume
@@ -85,11 +85,12 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
  * If already not suspended, this function allocates and sends a host
  * sleep activate request to the firmware and turns off the traffic.
  */
-static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+static int mwifiex_pcie_suspend(struct device *dev)
 {
        struct mwifiex_adapter *adapter;
        struct pcie_service_card *card;
        int hs_actived;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
                card = (struct pcie_service_card *) pci_get_drvdata(pdev);
@@ -120,10 +121,11 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
  * If already not resumed, this function turns on the traffic and
  * sends a host sleep cancel request to the firmware.
  */
-static int mwifiex_pcie_resume(struct pci_dev *pdev)
+static int mwifiex_pcie_resume(struct device *dev)
 {
        struct mwifiex_adapter *adapter;
        struct pcie_service_card *card;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
                card = (struct pcie_service_card *) pci_get_drvdata(pdev);
@@ -211,9 +213,9 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        wait_for_completion(&adapter->fw_load);
 
        if (user_rmmod) {
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
                if (adapter->is_suspended)
-                       mwifiex_pcie_resume(pdev);
+                       mwifiex_pcie_resume(&pdev->dev);
 #endif
 
                for (i = 0; i < adapter->priv_num; i++)
@@ -233,6 +235,14 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        kfree(card);
 }
 
+static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
+{
+       user_rmmod = 1;
+       mwifiex_pcie_remove(pdev);
+
+       return;
+}
+
 static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
        {
                PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
@@ -249,17 +259,24 @@ static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
 
 MODULE_DEVICE_TABLE(pci, mwifiex_ids);
 
+#ifdef CONFIG_PM_SLEEP
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend,
+                               mwifiex_pcie_resume);
+#endif
+
 /* PCI Device Driver */
 static struct pci_driver __refdata mwifiex_pcie = {
        .name     = "mwifiex_pcie",
        .id_table = mwifiex_ids,
        .probe    = mwifiex_pcie_probe,
        .remove   = mwifiex_pcie_remove,
-#ifdef CONFIG_PM
-       /* Power Management Hooks */
-       .suspend  = mwifiex_pcie_suspend,
-       .resume   = mwifiex_pcie_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver   = {
+               .pm = &mwifiex_pcie_pm_ops,
+       },
 #endif
+       .shutdown = mwifiex_pcie_shutdown,
 };
 
 /*
@@ -1925,7 +1942,7 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
                        ret = 0;
                        break;
                } else {
-                       mdelay(100);
+                       msleep(100);
                        ret = -1;
                }
        }
@@ -1937,12 +1954,10 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
                else if (!winner_status) {
                        dev_err(adapter->dev, "PCI-E is the winner\n");
                        adapter->winner = 1;
-                       ret = -1;
                } else {
                        dev_err(adapter->dev,
                                "PCI-E is not the winner <%#x,%d>, exit dnld\n",
                                ret, adapter->winner);
-                       ret = 0;
                }
        }
 
index c447d9b..8cf7d50 100644 (file)
@@ -543,6 +543,37 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
        return chan_idx;
 }
 
+/* This function appends rate TLV to scan config command. */
+static int
+mwifiex_append_rate_tlv(struct mwifiex_private *priv,
+                       struct mwifiex_scan_cmd_config *scan_cfg_out,
+                       u8 radio)
+{
+       struct mwifiex_ie_types_rates_param_set *rates_tlv;
+       u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos;
+       u32 rates_size;
+
+       memset(rates, 0, sizeof(rates));
+
+       tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
+
+       if (priv->scan_request)
+               rates_size = mwifiex_get_rates_from_cfg80211(priv, rates,
+                                                            radio);
+       else
+               rates_size = mwifiex_get_supported_rates(priv, rates);
+
+       dev_dbg(priv->adapter->dev, "info: SCAN_CMD: Rates size = %d\n",
+               rates_size);
+       rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos;
+       rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
+       rates_tlv->header.len = cpu_to_le16((u16) rates_size);
+       memcpy(rates_tlv->rates, rates, rates_size);
+       scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
+
+       return rates_size;
+}
+
 /*
  * This function constructs and sends multiple scan config commands to
  * the firmware.
@@ -564,9 +595,10 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
        struct mwifiex_chan_scan_param_set *tmp_chan_list;
        struct mwifiex_chan_scan_param_set *start_chan;
 
-       u32 tlv_idx;
+       u32 tlv_idx, rates_size;
        u32 total_scan_time;
        u32 done_early;
+       u8 radio_type;
 
        if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) {
                dev_dbg(priv->adapter->dev,
@@ -591,6 +623,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
 
                tlv_idx = 0;
                total_scan_time = 0;
+               radio_type = 0;
                chan_tlv_out->header.len = 0;
                start_chan = tmp_chan_list;
                done_early = false;
@@ -612,6 +645,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                                continue;
                        }
 
+                       radio_type = tmp_chan_list->radio_type;
                        dev_dbg(priv->adapter->dev,
                                "info: Scan: Chan(%3d), Radio(%d),"
                                " Mode(%d, %d), Dur(%d)\n",
@@ -692,6 +726,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                        break;
                }
 
+               rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out,
+                                                    radio_type);
+
                priv->adapter->scan_channels = start_chan;
 
                /* Send the scan command to the firmware with the specified
@@ -699,6 +736,14 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN,
                                             HostCmd_ACT_GEN_SET, 0,
                                             scan_cfg_out);
+
+               /* rate IE is updated per scan command but same starting
+                * pointer is used each time so that rate IE from earlier
+                * scan_cfg_out->buf is overwritten with new one.
+                */
+               scan_cfg_out->tlv_buf_len -=
+                           sizeof(struct mwifiex_ie_types_header) + rates_size;
+
                if (ret)
                        break;
        }
@@ -741,7 +786,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
        struct mwifiex_adapter *adapter = priv->adapter;
        struct mwifiex_ie_types_num_probes *num_probes_tlv;
        struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
-       struct mwifiex_ie_types_rates_param_set *rates_tlv;
        u8 *tlv_pos;
        u32 num_probes;
        u32 ssid_len;
@@ -753,8 +797,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
        u8 radio_type;
        int i;
        u8 ssid_filter;
-       u8 rates[MWIFIEX_SUPPORTED_RATES];
-       u32 rates_size;
        struct mwifiex_ie_types_htcap *ht_cap;
 
        /* The tlv_buf_len is calculated for each scan command.  The TLVs added
@@ -889,19 +931,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 
        }
 
-       /* Append rates tlv */
-       memset(rates, 0, sizeof(rates));
-
-       rates_size = mwifiex_get_supported_rates(priv, rates);
-
-       rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos;
-       rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
-       rates_tlv->header.len = cpu_to_le16((u16) rates_size);
-       memcpy(rates_tlv->rates, rates, rates_size);
-       tlv_pos += sizeof(rates_tlv->header) + rates_size;
-
-       dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size);
-
        if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
            (priv->adapter->config_bands & BAND_GN ||
             priv->adapter->config_bands & BAND_AN)) {
index 09185c9..0e2070f 100644 (file)
@@ -50,9 +50,6 @@ static struct mwifiex_if_ops sdio_ops;
 
 static struct semaphore add_remove_card_sem;
 
-static int mwifiex_sdio_resume(struct device *dev);
-static void mwifiex_sdio_interrupt(struct sdio_func *func);
-
 /*
  * SDIO probe.
  *
@@ -113,6 +110,51 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
 }
 
 /*
+ * SDIO resume.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_sdio_resume(struct device *dev)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct sdio_mmc_card *card;
+       struct mwifiex_adapter *adapter;
+       mmc_pm_flag_t pm_flag = 0;
+
+       if (func) {
+               pm_flag = sdio_get_host_pm_caps(func);
+               card = sdio_get_drvdata(func);
+               if (!card || !card->adapter) {
+                       pr_err("resume: invalid card or adapter\n");
+                       return 0;
+               }
+       } else {
+               pr_err("resume: sdio_func is not specified\n");
+               return 0;
+       }
+
+       adapter = card->adapter;
+
+       if (!adapter->is_suspended) {
+               dev_warn(adapter->dev, "device already resumed\n");
+               return 0;
+       }
+
+       adapter->is_suspended = false;
+
+       /* Disable Host Sleep */
+       mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+                         MWIFIEX_ASYNC_CMD);
+
+       return 0;
+}
+
+/*
  * SDIO remove.
  *
  * This function removes the interface and frees up the card structure.
@@ -212,51 +254,6 @@ static int mwifiex_sdio_suspend(struct device *dev)
        return ret;
 }
 
-/*
- * SDIO resume.
- *
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_sdio_resume(struct device *dev)
-{
-       struct sdio_func *func = dev_to_sdio_func(dev);
-       struct sdio_mmc_card *card;
-       struct mwifiex_adapter *adapter;
-       mmc_pm_flag_t pm_flag = 0;
-
-       if (func) {
-               pm_flag = sdio_get_host_pm_caps(func);
-               card = sdio_get_drvdata(func);
-               if (!card || !card->adapter) {
-                       pr_err("resume: invalid card or adapter\n");
-                       return 0;
-               }
-       } else {
-               pr_err("resume: sdio_func is not specified\n");
-               return 0;
-       }
-
-       adapter = card->adapter;
-
-       if (!adapter->is_suspended) {
-               dev_warn(adapter->dev, "device already resumed\n");
-               return 0;
-       }
-
-       adapter->is_suspended = false;
-
-       /* Disable Host Sleep */
-       mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
-                         MWIFIEX_ASYNC_CMD);
-
-       return 0;
-}
-
 /* Device ID for SD8786 */
 #define SDIO_DEVICE_ID_MARVELL_8786   (0x9116)
 /* Device ID for SD8787 */
@@ -707,6 +704,65 @@ static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
 }
 
 /*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+       struct sdio_mmc_card *card = adapter->card;
+       u8 sdio_ireg;
+       unsigned long flags;
+
+       if (mwifiex_read_data_sync(adapter, card->mp_regs,
+                                  card->reg->max_mp_regs,
+                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
+               dev_err(adapter->dev, "read mp_regs failed\n");
+               return;
+       }
+
+       sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
+       if (sdio_ireg) {
+               /*
+                * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+                * For SDIO new mode CMD port interrupts
+                *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+                *      UP_LD_CMD_PORT_HOST_INT_STATUS
+                * Clear the interrupt status register
+                */
+               dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
+               spin_lock_irqsave(&adapter->int_lock, flags);
+               adapter->int_status |= sdio_ireg;
+               spin_unlock_irqrestore(&adapter->int_lock, flags);
+       }
+}
+
+/*
+ * SDIO interrupt handler.
+ *
+ * This function reads the interrupt status from firmware and handles
+ * the interrupt in current thread (ksdioirqd) right away.
+ */
+static void
+mwifiex_sdio_interrupt(struct sdio_func *func)
+{
+       struct mwifiex_adapter *adapter;
+       struct sdio_mmc_card *card;
+
+       card = sdio_get_drvdata(func);
+       if (!card || !card->adapter) {
+               pr_debug("int: func=%p card=%p adapter=%p\n",
+                        func, card, card ? card->adapter : NULL);
+               return;
+       }
+       adapter = card->adapter;
+
+       if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
+               adapter->ps_state = PS_STATE_AWAKE;
+
+       mwifiex_interrupt_status(adapter);
+       mwifiex_main_process(adapter);
+}
+
+/*
  * This function enables the host interrupt.
  *
  * The host interrupt enable mask is written to the card
@@ -944,7 +1000,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
                        ret = 0;
                        break;
                } else {
-                       mdelay(100);
+                       msleep(100);
                        ret = -1;
                }
        }
@@ -963,65 +1019,6 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
 }
 
 /*
- * This function reads the interrupt status from card.
- */
-static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
-{
-       struct sdio_mmc_card *card = adapter->card;
-       u8 sdio_ireg;
-       unsigned long flags;
-
-       if (mwifiex_read_data_sync(adapter, card->mp_regs,
-                                  card->reg->max_mp_regs,
-                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
-               dev_err(adapter->dev, "read mp_regs failed\n");
-               return;
-       }
-
-       sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
-       if (sdio_ireg) {
-               /*
-                * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
-                * For SDIO new mode CMD port interrupts
-                *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
-                *      UP_LD_CMD_PORT_HOST_INT_STATUS
-                * Clear the interrupt status register
-                */
-               dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
-               spin_lock_irqsave(&adapter->int_lock, flags);
-               adapter->int_status |= sdio_ireg;
-               spin_unlock_irqrestore(&adapter->int_lock, flags);
-       }
-}
-
-/*
- * SDIO interrupt handler.
- *
- * This function reads the interrupt status from firmware and handles
- * the interrupt in current thread (ksdioirqd) right away.
- */
-static void
-mwifiex_sdio_interrupt(struct sdio_func *func)
-{
-       struct mwifiex_adapter *adapter;
-       struct sdio_mmc_card *card;
-
-       card = sdio_get_drvdata(func);
-       if (!card || !card->adapter) {
-               pr_debug("int: func=%p card=%p adapter=%p\n",
-                        func, card, card ? card->adapter : NULL);
-               return;
-       }
-       adapter = card->adapter;
-
-       if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
-               adapter->ps_state = PS_STATE_AWAKE;
-
-       mwifiex_interrupt_status(adapter);
-       mwifiex_main_process(adapter);
-}
-
-/*
  * This function decodes a received packet.
  *
  * Based on the type, the packet is treated as either a data, or
index 8ece485..9b75ed8 100644 (file)
@@ -707,8 +707,9 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
                if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
                        tlv_mac = (void *)((u8 *)&key_material->key_param_set +
                                           key_param_len);
-                       tlv_mac->tlv.type = cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
-                       tlv_mac->tlv.len = cpu_to_le16(ETH_ALEN);
+                       tlv_mac->header.type =
+                                       cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
+                       tlv_mac->header.len = cpu_to_le16(ETH_ALEN);
                        memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN);
                        cmd_size = key_param_len + S_DS_GEN +
                                   sizeof(key_material->action) +
index ea265ec..8b05752 100644 (file)
@@ -201,6 +201,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
        case EVENT_DEAUTHENTICATED:
                dev_dbg(adapter->dev, "event: Deauthenticated\n");
+               if (priv->wps.session_enable) {
+                       dev_dbg(adapter->dev,
+                               "info: receive deauth event in wps session\n");
+                       break;
+               }
                adapter->dbg.num_event_deauth++;
                if (priv->media_connected) {
                        reason_code =
@@ -211,6 +216,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
        case EVENT_DISASSOCIATED:
                dev_dbg(adapter->dev, "event: Disassociated\n");
+               if (priv->wps.session_enable) {
+                       dev_dbg(adapter->dev,
+                               "info: receive disassoc event in wps session\n");
+                       break;
+               }
                adapter->dbg.num_event_disassoc++;
                if (priv->media_connected) {
                        reason_code =
index 8af97ab..f084412 100644 (file)
@@ -797,15 +797,16 @@ static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
                               u8 *ie_data_ptr, u16 ie_len)
 {
        if (ie_len) {
-               priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
-               if (!priv->wps_ie)
-                       return -ENOMEM;
-               if (ie_len > sizeof(priv->wps_ie)) {
+               if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
                        dev_dbg(priv->adapter->dev,
                                "info: failed to copy WPS IE, too big\n");
-                       kfree(priv->wps_ie);
                        return -1;
                }
+
+               priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
+               if (!priv->wps_ie)
+                       return -ENOMEM;
+
                memcpy(priv->wps_ie, ie_data_ptr, ie_len);
                priv->wps_ie_len = ie_len;
                dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n",
index 2de882d..64424c8 100644 (file)
@@ -293,9 +293,9 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
        u8 *tlv = *tlv_buf;
 
        tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
-       tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
-       tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
-                                       sizeof(struct host_cmd_tlv));
+       tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
+       tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
+                                       sizeof(struct mwifiex_ie_types_header));
        tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
        tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
        cmd_size += sizeof(struct host_cmd_tlv_akmp);
@@ -303,10 +303,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
                pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
-               pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
-               pwk_cipher->tlv.len =
+               pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+               pwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
                pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
                cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
@@ -315,10 +315,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
                pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
-               pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
-               pwk_cipher->tlv.len =
+               pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+               pwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
                pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
                cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
@@ -327,10 +327,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
                gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
-               gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
-               gwk_cipher->tlv.len =
+               gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
+               gwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
                cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
                tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
@@ -338,13 +338,15 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.length) {
                passphrase = (struct host_cmd_tlv_passphrase *)tlv;
-               passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
-               passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
+               passphrase->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
+               passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
                memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
                       bss_cfg->wpa_cfg.length);
-               cmd_size += sizeof(struct host_cmd_tlv) +
+               cmd_size += sizeof(struct mwifiex_ie_types_header) +
                            bss_cfg->wpa_cfg.length;
-               tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length;
+               tlv += sizeof(struct mwifiex_ie_types_header) +
+                               bss_cfg->wpa_cfg.length;
        }
 
        *param_size = cmd_size;
@@ -403,16 +405,17 @@ mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
                    (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
                     bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
                        wep_key = (struct host_cmd_tlv_wep_key *)tlv;
-                       wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
-                       wep_key->tlv.len =
+                       wep_key->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
+                       wep_key->header.len =
                                cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
                        wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
                        wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
                        memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
                               bss_cfg->wep_cfg[i].length);
-                       cmd_size += sizeof(struct host_cmd_tlv) + 2 +
+                       cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
                                    bss_cfg->wep_cfg[i].length;
-                       tlv += sizeof(struct host_cmd_tlv) + 2 +
+                       tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
                                    bss_cfg->wep_cfg[i].length;
                }
        }
@@ -449,16 +452,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->ssid.ssid_len) {
                ssid = (struct host_cmd_tlv_ssid *)tlv;
-               ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
-               ssid->tlv.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
+               ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
+               ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
                memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
-               cmd_size += sizeof(struct host_cmd_tlv) +
+               cmd_size += sizeof(struct mwifiex_ie_types_header) +
                            bss_cfg->ssid.ssid_len;
-               tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len;
+               tlv += sizeof(struct mwifiex_ie_types_header) +
+                               bss_cfg->ssid.ssid_len;
 
                bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
-               bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
-               bcast_ssid->tlv.len =
+               bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
+               bcast_ssid->header.len =
                                cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
                bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
                cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
@@ -466,13 +470,13 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        }
        if (bss_cfg->rates[0]) {
                tlv_rates = (struct host_cmd_tlv_rates *)tlv;
-               tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
+               tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
 
                for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
                     i++)
                        tlv_rates->rates[i] = bss_cfg->rates[i];
 
-               tlv_rates->tlv.len = cpu_to_le16(i);
+               tlv_rates->header.len = cpu_to_le16(i);
                cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
                tlv += sizeof(struct host_cmd_tlv_rates) + i;
        }
@@ -482,10 +486,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
            (bss_cfg->band_cfg == BAND_CONFIG_A &&
             bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
                chan_band = (struct host_cmd_tlv_channel_band *)tlv;
-               chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
-               chan_band->tlv.len =
+               chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+               chan_band->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                chan_band->band_config = bss_cfg->band_cfg;
                chan_band->channel = bss_cfg->channel;
                cmd_size += sizeof(struct host_cmd_tlv_channel_band);
@@ -494,11 +498,11 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
            bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
                beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
-               beacon_period->tlv.type =
+               beacon_period->header.type =
                                        cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
-               beacon_period->tlv.len =
+               beacon_period->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
                cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
                tlv += sizeof(struct host_cmd_tlv_beacon_period);
@@ -506,21 +510,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
            bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
                dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
-               dtim_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
-               dtim_period->tlv.len =
+               dtim_period->header.type =
+                       cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
+               dtim_period->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                dtim_period->period = bss_cfg->dtim_period;
                cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
                tlv += sizeof(struct host_cmd_tlv_dtim_period);
        }
        if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
                rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
-               rts_threshold->tlv.type =
+               rts_threshold->header.type =
                                        cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
-               rts_threshold->tlv.len =
+               rts_threshold->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
                cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
                tlv += sizeof(struct host_cmd_tlv_frag_threshold);
@@ -528,21 +533,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
            (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
                frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
-               frag_threshold->tlv.type =
+               frag_threshold->header.type =
                                cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
-               frag_threshold->tlv.len =
+               frag_threshold->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
                cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
                tlv += sizeof(struct host_cmd_tlv_frag_threshold);
        }
        if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
                retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
-               retry_limit->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
-               retry_limit->tlv.len =
+               retry_limit->header.type =
+                       cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
+               retry_limit->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                retry_limit->limit = (u8)bss_cfg->retry_limit;
                cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
                tlv += sizeof(struct host_cmd_tlv_retry_limit);
@@ -557,21 +563,21 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
            (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
                auth_type = (struct host_cmd_tlv_auth_type *)tlv;
-               auth_type->tlv.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
-               auth_type->tlv.len =
+               auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+               auth_type->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
-                       sizeof(struct host_cmd_tlv));
+                       sizeof(struct mwifiex_ie_types_header));
                auth_type->auth_type = (u8)bss_cfg->auth_mode;
                cmd_size += sizeof(struct host_cmd_tlv_auth_type);
                tlv += sizeof(struct host_cmd_tlv_auth_type);
        }
        if (bss_cfg->protocol) {
                encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
-               encrypt_protocol->tlv.type =
+               encrypt_protocol->header.type =
                        cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
-               encrypt_protocol->tlv.len =
+               encrypt_protocol->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
-                       - sizeof(struct host_cmd_tlv));
+                       - sizeof(struct mwifiex_ie_types_header));
                encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
                cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
                tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
@@ -608,9 +614,9 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->sta_ao_timer) {
                ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
-               ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
-               ao_timer->tlv.len = cpu_to_le16(sizeof(*ao_timer) -
-                                               sizeof(struct host_cmd_tlv));
+               ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
+               ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
+                                       sizeof(struct mwifiex_ie_types_header));
                ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
                cmd_size += sizeof(*ao_timer);
                tlv += sizeof(*ao_timer);
@@ -618,9 +624,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->ps_sta_ao_timer) {
                ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
-               ps_ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
-               ps_ao_timer->tlv.len = cpu_to_le16(sizeof(*ps_ao_timer) -
-                                                  sizeof(struct host_cmd_tlv));
+               ps_ao_timer->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
+               ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
+                               sizeof(struct mwifiex_ie_types_header));
                ps_ao_timer->sta_ao_timer =
                                        cpu_to_le32(bss_cfg->ps_sta_ao_timer);
                cmd_size += sizeof(*ps_ao_timer);
@@ -636,16 +643,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
 {
        struct mwifiex_ie_list *ap_ie = cmd_buf;
-       struct host_cmd_tlv *tlv_ie = (struct host_cmd_tlv *)tlv;
+       struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
 
        if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
                return -1;
 
-       *ie_size += le16_to_cpu(ap_ie->len) + sizeof(struct host_cmd_tlv);
+       *ie_size += le16_to_cpu(ap_ie->len) +
+                       sizeof(struct mwifiex_ie_types_header);
 
        tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
        tlv_ie->len = ap_ie->len;
-       tlv += sizeof(struct host_cmd_tlv);
+       tlv += sizeof(struct mwifiex_ie_types_header);
 
        memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
 
index a018e42..1cfe5a7 100644 (file)
 #include "11n_aggr.h"
 #include "11n_rxreorder.h"
 
+/* This function checks if particular RA list has packets more than low bridge
+ * packet threshold and then deletes packet from this RA list.
+ * Function deletes packets from such RA list and returns true. If no such list
+ * is found, false is returned.
+ */
+static bool
+mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
+                                 struct list_head *ra_list_head)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       struct sk_buff *skb, *tmp;
+       bool pkt_deleted = false;
+       struct mwifiex_txinfo *tx_info;
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       list_for_each_entry(ra_list, ra_list_head, list) {
+               if (skb_queue_empty(&ra_list->skb_head))
+                       continue;
+
+               skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+                       tx_info = MWIFIEX_SKB_TXCB(skb);
+                       if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
+                               __skb_unlink(skb, &ra_list->skb_head);
+                               mwifiex_write_data_complete(adapter, skb, 0,
+                                                           -1);
+                               atomic_dec(&priv->wmm.tx_pkts_queued);
+                               pkt_deleted = true;
+                       }
+                       if ((atomic_read(&adapter->pending_bridged_pkts) <=
+                                            MWIFIEX_BRIDGED_PKTS_THR_LOW))
+                               break;
+               }
+       }
+
+       return pkt_deleted;
+}
+
+/* This function deletes packets from particular RA List. RA list index
+ * from which packets are deleted is preserved so that packets from next RA
+ * list are deleted upon subsequent call thus maintaining fairness.
+ */
+static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
+{
+       unsigned long flags;
+       struct list_head *ra_list;
+       int i;
+
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
+               if (priv->del_list_idx == MAX_NUM_TID)
+                       priv->del_list_idx = 0;
+               ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
+               if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) {
+                       priv->del_list_idx++;
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+
 static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
                                         struct sk_buff *skb)
 {
@@ -40,10 +103,11 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
 
        if ((atomic_read(&adapter->pending_bridged_pkts) >=
-                                            MWIFIEX_BRIDGED_PKTS_THRESHOLD)) {
+                                            MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
                dev_err(priv->adapter->dev,
                        "Tx: Bridge packet limit reached. Drop packet!\n");
                kfree_skb(skb);
+               mwifiex_uap_cleanup_tx_queues(priv);
                return;
        }
 
@@ -95,10 +159,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        atomic_inc(&adapter->tx_pending);
        atomic_inc(&adapter->pending_bridged_pkts);
 
-       if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) {
-               mwifiex_set_trans_start(priv->netdev);
-               mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
-       }
        return;
 }
 
index f90fe21..fca98b5 100644 (file)
@@ -786,6 +786,13 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        return 0;
 }
 
+static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+
+       usb_set_intfdata(card->intf, NULL);
+}
+
 static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                    struct mwifiex_fw_image *fw)
 {
@@ -978,6 +985,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
 
 static struct mwifiex_if_ops usb_ops = {
        .register_dev =         mwifiex_register_dev,
+       .unregister_dev =       mwifiex_unregister_dev,
        .wakeup =               mwifiex_pm_wakeup_card,
        .wakeup_complete =      mwifiex_pm_wakeup_card_complete,
 
index 944e884..2e8f9cd 100644 (file)
@@ -120,7 +120,7 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra)
 
        memcpy(ra_list->ra, ra, ETH_ALEN);
 
-       ra_list->total_pkts_size = 0;
+       ra_list->total_pkt_count = 0;
 
        dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list);
 
@@ -188,7 +188,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
                        ra_list, ra_list->is_11n_enabled);
 
                if (ra_list->is_11n_enabled) {
-                       ra_list->pkt_count = 0;
+                       ra_list->ba_pkt_count = 0;
                        ra_list->ba_packet_thr =
                                              mwifiex_get_random_ba_threshold();
                }
@@ -679,8 +679,8 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
 
        skb_queue_tail(&ra_list->skb_head, skb);
 
-       ra_list->total_pkts_size += skb->len;
-       ra_list->pkt_count++;
+       ra_list->ba_pkt_count++;
+       ra_list->total_pkt_count++;
 
        if (atomic_read(&priv->wmm.highest_queued_prio) <
                                                tos_to_tid_inv[tid_down])
@@ -1037,7 +1037,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv,
        tx_info = MWIFIEX_SKB_TXCB(skb);
        dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb);
 
-       ptr->total_pkts_size -= skb->len;
+       ptr->total_pkt_count--;
 
        if (!skb_queue_empty(&ptr->skb_head))
                skb_next = skb_peek(&ptr->skb_head);
@@ -1062,8 +1062,8 @@ mwifiex_send_single_packet(struct mwifiex_private *priv,
 
                skb_queue_tail(&ptr->skb_head, skb);
 
-               ptr->total_pkts_size += skb->len;
-               ptr->pkt_count++;
+               ptr->total_pkt_count++;
+               ptr->ba_pkt_count++;
                tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
                spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
                                       ra_list_flags);
@@ -1224,7 +1224,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                   mwifiex_send_single_packet() */
        } else {
                if (mwifiex_is_ampdu_allowed(priv, tid) &&
-                   ptr->pkt_count > ptr->ba_packet_thr) {
+                   ptr->ba_pkt_count > ptr->ba_packet_thr) {
                        if (mwifiex_space_avail_for_new_ba_stream(adapter)) {
                                mwifiex_create_ba_tbl(priv, ptr->ra, tid,
                                                      BA_SETUP_INPROGRESS);
index 3e60a31..68dbbb9 100644 (file)
@@ -166,6 +166,12 @@ config RT2800USB_RT35XX
          rt2800usb driver.
          Supported chips: RT3572
 
+config RT2800USB_RT3573
+       bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)"
+       ---help---
+         This enables support for RT3573 chipset based wireless USB devices
+         in the rt2800usb driver.
+
 config RT2800USB_RT53XX
        bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)"
        ---help---
index d78c495..a313241 100644 (file)
@@ -88,6 +88,7 @@
 #define REV_RT3071E                    0x0211
 #define REV_RT3090E                    0x0211
 #define REV_RT3390E                    0x0211
+#define REV_RT3593E                    0x0211
 #define REV_RT5390F                    0x0502
 #define REV_RT5390R                    0x1502
 #define REV_RT5592C                    0x0221
 #define TX_PWR_CFG_0_9MBS              FIELD32(0x00f00000)
 #define TX_PWR_CFG_0_12MBS             FIELD32(0x0f000000)
 #define TX_PWR_CFG_0_18MBS             FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_0_CCK1_CH0          FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_CCK1_CH1          FIELD32(0x000000f0)
+#define TX_PWR_CFG_0_CCK5_CH0          FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_CCK5_CH1          FIELD32(0x0000f000)
+#define TX_PWR_CFG_0_OFDM6_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_OFDM6_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_0_OFDM12_CH0                FIELD32(0x0f000000)
+#define TX_PWR_CFG_0_OFDM12_CH1                FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_1:
 #define TX_PWR_CFG_1_MCS1              FIELD32(0x00f00000)
 #define TX_PWR_CFG_1_MCS2              FIELD32(0x0f000000)
 #define TX_PWR_CFG_1_MCS3              FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_1_OFDM24_CH0                FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_OFDM24_CH1                FIELD32(0x000000f0)
+#define TX_PWR_CFG_1_OFDM48_CH0                FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_OFDM48_CH1                FIELD32(0x0000f000)
+#define TX_PWR_CFG_1_MCS0_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_MCS0_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_1_MCS2_CH0          FIELD32(0x0f000000)
+#define TX_PWR_CFG_1_MCS2_CH1          FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_2:
 #define TX_PWR_CFG_2_MCS9              FIELD32(0x00f00000)
 #define TX_PWR_CFG_2_MCS10             FIELD32(0x0f000000)
 #define TX_PWR_CFG_2_MCS11             FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_2_MCS4_CH0          FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_MCS4_CH1          FIELD32(0x000000f0)
+#define TX_PWR_CFG_2_MCS6_CH0          FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_MCS6_CH1          FIELD32(0x0000f000)
+#define TX_PWR_CFG_2_MCS8_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_MCS8_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_2_MCS10_CH0         FIELD32(0x0f000000)
+#define TX_PWR_CFG_2_MCS10_CH1         FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_3:
 #define TX_PWR_CFG_3_UKNOWN2           FIELD32(0x00f00000)
 #define TX_PWR_CFG_3_UKNOWN3           FIELD32(0x0f000000)
 #define TX_PWR_CFG_3_UKNOWN4           FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_MCS12_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_MCS12_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_MCS14_CH0         FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_MCS14_CH1         FIELD32(0x0000f000)
+#define TX_PWR_CFG_3_STBC0_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_STBC0_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_3_STBC2_CH0         FIELD32(0x0f000000)
+#define TX_PWR_CFG_3_STBC2_CH1         FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_4:
 #define TX_PWR_CFG_4_UKNOWN6           FIELD32(0x000000f0)
 #define TX_PWR_CFG_4_UKNOWN7           FIELD32(0x00000f00)
 #define TX_PWR_CFG_4_UKNOWN8           FIELD32(0x0000f000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_STBC4_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_STBC4_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_STBC6_CH0         FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_STBC6_CH1         FIELD32(0x0000f000)
 
 /*
  * TX_PIN_CFG:
  */
 #define EXP_ACK_TIME                   0x1380
 
+/* TX_PWR_CFG_5 */
+#define TX_PWR_CFG_5                   0x1384
+#define TX_PWR_CFG_5_MCS16_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_5_MCS16_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_5_MCS16_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_5_MCS18_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_5_MCS18_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_5_MCS18_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_6 */
+#define TX_PWR_CFG_6                   0x1388
+#define TX_PWR_CFG_6_MCS20_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_6_MCS20_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_6_MCS20_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_6_MCS22_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_6_MCS22_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_6_MCS22_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_0_EXT */
+#define TX_PWR_CFG_0_EXT               0x1390
+#define TX_PWR_CFG_0_EXT_CCK1_CH2      FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_EXT_CCK5_CH2      FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_EXT_OFDM6_CH2     FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_EXT_OFDM12_CH2    FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_1_EXT */
+#define TX_PWR_CFG_1_EXT               0x1394
+#define TX_PWR_CFG_1_EXT_OFDM24_CH2    FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_EXT_OFDM48_CH2    FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_EXT_MCS0_CH2      FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_EXT_MCS2_CH2      FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_2_EXT */
+#define TX_PWR_CFG_2_EXT               0x1398
+#define TX_PWR_CFG_2_EXT_MCS4_CH2      FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_EXT_MCS6_CH2      FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_EXT_MCS8_CH2      FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_EXT_MCS10_CH2     FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_3_EXT */
+#define TX_PWR_CFG_3_EXT               0x139c
+#define TX_PWR_CFG_3_EXT_MCS12_CH2     FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_EXT_MCS14_CH2     FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_EXT_STBC0_CH2     FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_EXT_STBC2_CH2     FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_4_EXT */
+#define TX_PWR_CFG_4_EXT               0x13a0
+#define TX_PWR_CFG_4_EXT_STBC4_CH2     FIELD32(0x0000000f)
+#define TX_PWR_CFG_4_EXT_STBC6_CH2     FIELD32(0x00000f00)
+
+/* TX_PWR_CFG_7 */
+#define TX_PWR_CFG_7                   0x13d4
+#define TX_PWR_CFG_7_OFDM54_CH0                FIELD32(0x0000000f)
+#define TX_PWR_CFG_7_OFDM54_CH1                FIELD32(0x000000f0)
+#define TX_PWR_CFG_7_OFDM54_CH2                FIELD32(0x00000f00)
+#define TX_PWR_CFG_7_MCS7_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_7_MCS7_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_7_MCS7_CH2          FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_8 */
+#define TX_PWR_CFG_8                   0x13d8
+#define TX_PWR_CFG_8_MCS15_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_8_MCS15_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_8_MCS15_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_8_MCS23_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_8_MCS23_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_8_MCS23_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_9 */
+#define TX_PWR_CFG_9                   0x13dc
+#define TX_PWR_CFG_9_STBC7_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_9_STBC7_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_9_STBC7_CH2         FIELD32(0x00000f00)
+
 /*
  * RX_FILTER_CFG: RX configuration register.
  */
@@ -1975,6 +2092,10 @@ struct mac_iveiv_entry {
 #define BBP109_TX0_POWER               FIELD8(0x0f)
 #define BBP109_TX1_POWER               FIELD8(0xf0)
 
+/* BBP 110 */
+#define BBP110_TX2_POWER               FIELD8(0x0f)
+
+
 /*
  * BBP 138: Unknown
  */
@@ -2024,6 +2145,12 @@ struct mac_iveiv_entry {
 #define RFCSR3_PA2_CASCODE_BIAS_CCKK   FIELD8(0x80)
 /* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */
 #define RFCSR3_VCOCAL_EN               FIELD8(0x80)
+/* Bits for RF3050 */
+#define RFCSR3_BIT1                    FIELD8(0x02)
+#define RFCSR3_BIT2                    FIELD8(0x04)
+#define RFCSR3_BIT3                    FIELD8(0x08)
+#define RFCSR3_BIT4                    FIELD8(0x10)
+#define RFCSR3_BIT5                    FIELD8(0x20)
 
 /*
  * FRCSR 5:
@@ -2036,6 +2163,8 @@ struct mac_iveiv_entry {
 #define RFCSR6_R1                      FIELD8(0x03)
 #define RFCSR6_R2                      FIELD8(0x40)
 #define RFCSR6_TXDIV           FIELD8(0x0c)
+/* bits for RF3053 */
+#define RFCSR6_VCO_IC                  FIELD8(0xc0)
 
 /*
  * RFCSR 7:
@@ -2060,7 +2189,12 @@ struct mac_iveiv_entry {
  * RFCSR 11:
  */
 #define RFCSR11_R                      FIELD8(0x03)
+#define RFCSR11_PLL_MOD                        FIELD8(0x0c)
 #define RFCSR11_MOD                    FIELD8(0xc0)
+/* bits for RF3053 */
+/* TODO: verify RFCSR11_MOD usage on other chips */
+#define RFCSR11_PLL_IDOH               FIELD8(0x40)
+
 
 /*
  * RFCSR 12:
@@ -2092,6 +2226,10 @@ struct mac_iveiv_entry {
 #define RFCSR17_R                      FIELD8(0x20)
 #define RFCSR17_CODE                   FIELD8(0x7f)
 
+/* RFCSR 18 */
+#define RFCSR18_XO_TUNE_BYPASS         FIELD8(0x40)
+
+
 /*
  * RFCSR 20:
  */
@@ -2152,6 +2290,12 @@ struct mac_iveiv_entry {
 #define RFCSR31_RX_H20M                        FIELD8(0x20)
 #define RFCSR31_RX_CALIB               FIELD8(0x7f)
 
+/* RFCSR 32 bits for RF3053 */
+#define RFCSR32_TX_AGC_FC              FIELD8(0xf8)
+
+/* RFCSR 36 bits for RF3053 */
+#define RFCSR36_RF_BS                  FIELD8(0x80)
+
 /*
  * RFCSR 38:
  */
@@ -2160,6 +2304,7 @@ struct mac_iveiv_entry {
 /*
  * RFCSR 39:
  */
+#define RFCSR39_RX_DIV                 FIELD8(0x40)
 #define RFCSR39_RX_LO2_EN              FIELD8(0x80)
 
 /*
@@ -2167,12 +2312,36 @@ struct mac_iveiv_entry {
  */
 #define RFCSR49_TX                     FIELD8(0x3f)
 #define RFCSR49_EP                     FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR49_TX_LO1_IC              FIELD8(0x1c)
+#define RFCSR49_TX_DIV                 FIELD8(0x20)
 
 /*
  * RFCSR 50:
  */
 #define RFCSR50_TX                     FIELD8(0x3f)
 #define RFCSR50_EP                     FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR50_TX_LO1_EN              FIELD8(0x20)
+#define RFCSR50_TX_LO2_EN              FIELD8(0x10)
+
+/* RFCSR 51 */
+/* bits for RT3593 */
+#define RFCSR51_BITS01                 FIELD8(0x03)
+#define RFCSR51_BITS24                 FIELD8(0x1c)
+#define RFCSR51_BITS57                 FIELD8(0xe0)
+
+#define RFCSR53_TX_POWER               FIELD8(0x3f)
+#define RFCSR53_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR54_TX_POWER               FIELD8(0x3f)
+#define RFCSR54_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR55_TX_POWER               FIELD8(0x3f)
+#define RFCSR55_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR57_DRV_CC                 FIELD8(0xfc)
+
 
 /*
  * RF registers
@@ -2206,28 +2375,67 @@ struct mac_iveiv_entry {
  * The wordsize of the EEPROM is 16 bits.
  */
 
-/*
- * Chip ID
- */
-#define EEPROM_CHIP_ID                 0x0000
+enum rt2800_eeprom_word {
+       EEPROM_CHIP_ID = 0,
+       EEPROM_VERSION,
+       EEPROM_MAC_ADDR_0,
+       EEPROM_MAC_ADDR_1,
+       EEPROM_MAC_ADDR_2,
+       EEPROM_NIC_CONF0,
+       EEPROM_NIC_CONF1,
+       EEPROM_FREQ,
+       EEPROM_LED_AG_CONF,
+       EEPROM_LED_ACT_CONF,
+       EEPROM_LED_POLARITY,
+       EEPROM_NIC_CONF2,
+       EEPROM_LNA,
+       EEPROM_RSSI_BG,
+       EEPROM_RSSI_BG2,
+       EEPROM_TXMIXER_GAIN_BG,
+       EEPROM_RSSI_A,
+       EEPROM_RSSI_A2,
+       EEPROM_TXMIXER_GAIN_A,
+       EEPROM_EIRP_MAX_TX_POWER,
+       EEPROM_TXPOWER_DELTA,
+       EEPROM_TXPOWER_BG1,
+       EEPROM_TXPOWER_BG2,
+       EEPROM_TSSI_BOUND_BG1,
+       EEPROM_TSSI_BOUND_BG2,
+       EEPROM_TSSI_BOUND_BG3,
+       EEPROM_TSSI_BOUND_BG4,
+       EEPROM_TSSI_BOUND_BG5,
+       EEPROM_TXPOWER_A1,
+       EEPROM_TXPOWER_A2,
+       EEPROM_TSSI_BOUND_A1,
+       EEPROM_TSSI_BOUND_A2,
+       EEPROM_TSSI_BOUND_A3,
+       EEPROM_TSSI_BOUND_A4,
+       EEPROM_TSSI_BOUND_A5,
+       EEPROM_TXPOWER_BYRATE,
+       EEPROM_BBP_START,
+
+       /* IDs for extended EEPROM format used by three-chain devices */
+       EEPROM_EXT_LNA2,
+       EEPROM_EXT_TXPOWER_BG3,
+       EEPROM_EXT_TXPOWER_A3,
+
+       /* New values must be added before this */
+       EEPROM_WORD_COUNT
+};
 
 /*
  * EEPROM Version
  */
-#define EEPROM_VERSION                 0x0001
 #define EEPROM_VERSION_FAE             FIELD16(0x00ff)
 #define EEPROM_VERSION_VERSION         FIELD16(0xff00)
 
 /*
  * HW MAC address.
  */
-#define EEPROM_MAC_ADDR_0              0x0002
 #define EEPROM_MAC_ADDR_BYTE0          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE1          FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_1              0x0003
 #define EEPROM_MAC_ADDR_BYTE2          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE3          FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_2              0x0004
 #define EEPROM_MAC_ADDR_BYTE4          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE5          FIELD16(0xff00)
 
@@ -2237,7 +2445,6 @@ struct mac_iveiv_entry {
  * TXPATH: 1: 1T, 2: 2T, 3: 3T
  * RF_TYPE: RFIC type
  */
-#define        EEPROM_NIC_CONF0                0x001a
 #define EEPROM_NIC_CONF0_RXPATH                FIELD16(0x000f)
 #define EEPROM_NIC_CONF0_TXPATH                FIELD16(0x00f0)
 #define EEPROM_NIC_CONF0_RF_TYPE               FIELD16(0x0f00)
@@ -2261,7 +2468,6 @@ struct mac_iveiv_entry {
  * BT_COEXIST: 0: disable, 1: enable
  * DAC_TEST: 0: disable, 1: enable
  */
-#define        EEPROM_NIC_CONF1                0x001b
 #define EEPROM_NIC_CONF1_HW_RADIO              FIELD16(0x0001)
 #define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC               FIELD16(0x0002)
 #define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G               FIELD16(0x0004)
@@ -2281,7 +2487,6 @@ struct mac_iveiv_entry {
 /*
  * EEPROM frequency
  */
-#define        EEPROM_FREQ                     0x001d
 #define EEPROM_FREQ_OFFSET             FIELD16(0x00ff)
 #define EEPROM_FREQ_LED_MODE           FIELD16(0x7f00)
 #define EEPROM_FREQ_LED_POLARITY       FIELD16(0x1000)
@@ -2298,9 +2503,6 @@ struct mac_iveiv_entry {
  * POLARITY_GPIO_4: Polarity GPIO4 setting.
  * LED_MODE: Led mode.
  */
-#define EEPROM_LED_AG_CONF             0x001e
-#define EEPROM_LED_ACT_CONF            0x001f
-#define EEPROM_LED_POLARITY            0x0020
 #define EEPROM_LED_POLARITY_RDY_BG     FIELD16(0x0001)
 #define EEPROM_LED_POLARITY_RDY_A      FIELD16(0x0002)
 #define EEPROM_LED_POLARITY_ACT                FIELD16(0x0004)
@@ -2317,7 +2519,6 @@ struct mac_iveiv_entry {
  * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
  * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
  */
-#define EEPROM_NIC_CONF2               0x0021
 #define EEPROM_NIC_CONF2_RX_STREAM             FIELD16(0x000f)
 #define EEPROM_NIC_CONF2_TX_STREAM             FIELD16(0x00f0)
 #define EEPROM_NIC_CONF2_CRYSTAL               FIELD16(0x0600)
@@ -2325,54 +2526,46 @@ struct mac_iveiv_entry {
 /*
  * EEPROM LNA
  */
-#define EEPROM_LNA                     0x0022
 #define EEPROM_LNA_BG                  FIELD16(0x00ff)
 #define EEPROM_LNA_A0                  FIELD16(0xff00)
 
 /*
  * EEPROM RSSI BG offset
  */
-#define EEPROM_RSSI_BG                 0x0023
 #define EEPROM_RSSI_BG_OFFSET0         FIELD16(0x00ff)
 #define EEPROM_RSSI_BG_OFFSET1         FIELD16(0xff00)
 
 /*
  * EEPROM RSSI BG2 offset
  */
-#define EEPROM_RSSI_BG2                        0x0024
 #define EEPROM_RSSI_BG2_OFFSET2                FIELD16(0x00ff)
 #define EEPROM_RSSI_BG2_LNA_A1         FIELD16(0xff00)
 
 /*
  * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2).
  */
-#define EEPROM_TXMIXER_GAIN_BG         0x0024
 #define EEPROM_TXMIXER_GAIN_BG_VAL     FIELD16(0x0007)
 
 /*
  * EEPROM RSSI A offset
  */
-#define EEPROM_RSSI_A                  0x0025
 #define EEPROM_RSSI_A_OFFSET0          FIELD16(0x00ff)
 #define EEPROM_RSSI_A_OFFSET1          FIELD16(0xff00)
 
 /*
  * EEPROM RSSI A2 offset
  */
-#define EEPROM_RSSI_A2                 0x0026
 #define EEPROM_RSSI_A2_OFFSET2         FIELD16(0x00ff)
 #define EEPROM_RSSI_A2_LNA_A2          FIELD16(0xff00)
 
 /*
  * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2).
  */
-#define EEPROM_TXMIXER_GAIN_A          0x0026
 #define EEPROM_TXMIXER_GAIN_A_VAL      FIELD16(0x0007)
 
 /*
  * EEPROM EIRP Maximum TX power values(unit: dbm)
  */
-#define EEPROM_EIRP_MAX_TX_POWER       0x0027
 #define EEPROM_EIRP_MAX_TX_POWER_2GHZ  FIELD16(0x00ff)
 #define EEPROM_EIRP_MAX_TX_POWER_5GHZ  FIELD16(0xff00)
 
@@ -2383,7 +2576,6 @@ struct mac_iveiv_entry {
  * TYPE: 1: Plus the delta value, 0: minus the delta value
  * ENABLE: enable tx power compensation for 40BW
  */
-#define EEPROM_TXPOWER_DELTA           0x0028
 #define EEPROM_TXPOWER_DELTA_VALUE_2G  FIELD16(0x003f)
 #define EEPROM_TXPOWER_DELTA_TYPE_2G   FIELD16(0x0040)
 #define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080)
@@ -2394,8 +2586,6 @@ struct mac_iveiv_entry {
 /*
  * EEPROM TXPOWER 802.11BG
  */
-#define        EEPROM_TXPOWER_BG1              0x0029
-#define        EEPROM_TXPOWER_BG2              0x0030
 #define EEPROM_TXPOWER_BG_SIZE         7
 #define EEPROM_TXPOWER_BG_1            FIELD16(0x00ff)
 #define EEPROM_TXPOWER_BG_2            FIELD16(0xff00)
@@ -2407,7 +2597,6 @@ struct mac_iveiv_entry {
  * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -3)
  */
-#define EEPROM_TSSI_BOUND_BG1          0x0037
 #define EEPROM_TSSI_BOUND_BG1_MINUS4   FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG1_MINUS3   FIELD16(0xff00)
 
@@ -2418,7 +2607,6 @@ struct mac_iveiv_entry {
  * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -1)
  */
-#define EEPROM_TSSI_BOUND_BG2          0x0038
 #define EEPROM_TSSI_BOUND_BG2_MINUS2   FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG2_MINUS1   FIELD16(0xff00)
 
@@ -2428,7 +2616,6 @@ struct mac_iveiv_entry {
  * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 1)
  */
-#define EEPROM_TSSI_BOUND_BG3          0x0039
 #define EEPROM_TSSI_BOUND_BG3_REF      FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG3_PLUS1    FIELD16(0xff00)
 
@@ -2439,7 +2626,6 @@ struct mac_iveiv_entry {
  * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 3)
  */
-#define EEPROM_TSSI_BOUND_BG4          0x003a
 #define EEPROM_TSSI_BOUND_BG4_PLUS2    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG4_PLUS3    FIELD16(0xff00)
 
@@ -2449,19 +2635,20 @@ struct mac_iveiv_entry {
  *        increased by (agc_step * 4)
  * AGC_STEP: Temperature compensation step.
  */
-#define EEPROM_TSSI_BOUND_BG5          0x003b
 #define EEPROM_TSSI_BOUND_BG5_PLUS4    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00)
 
 /*
  * EEPROM TXPOWER 802.11A
  */
-#define EEPROM_TXPOWER_A1              0x003c
-#define EEPROM_TXPOWER_A2              0x0053
 #define EEPROM_TXPOWER_A_SIZE          6
 #define EEPROM_TXPOWER_A_1             FIELD16(0x00ff)
 #define EEPROM_TXPOWER_A_2             FIELD16(0xff00)
 
+/* EEPROM_TXPOWER_{A,G} fields for RT3593 */
+#define EEPROM_TXPOWER_ALC             FIELD8(0x1f)
+#define EEPROM_TXPOWER_FINE_CTRL       FIELD8(0xe0)
+
 /*
  * EEPROM temperature compensation boundaries 802.11A
  * MINUS4: If the actual TSSI is below this boundary, tx power needs to be
@@ -2469,7 +2656,6 @@ struct mac_iveiv_entry {
  * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -3)
  */
-#define EEPROM_TSSI_BOUND_A1           0x006a
 #define EEPROM_TSSI_BOUND_A1_MINUS4    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A1_MINUS3    FIELD16(0xff00)
 
@@ -2480,7 +2666,6 @@ struct mac_iveiv_entry {
  * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -1)
  */
-#define EEPROM_TSSI_BOUND_A2           0x006b
 #define EEPROM_TSSI_BOUND_A2_MINUS2    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A2_MINUS1    FIELD16(0xff00)
 
@@ -2490,7 +2675,6 @@ struct mac_iveiv_entry {
  * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 1)
  */
-#define EEPROM_TSSI_BOUND_A3           0x006c
 #define EEPROM_TSSI_BOUND_A3_REF       FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A3_PLUS1     FIELD16(0xff00)
 
@@ -2501,7 +2685,6 @@ struct mac_iveiv_entry {
  * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 3)
  */
-#define EEPROM_TSSI_BOUND_A4           0x006d
 #define EEPROM_TSSI_BOUND_A4_PLUS2     FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A4_PLUS3     FIELD16(0xff00)
 
@@ -2511,14 +2694,12 @@ struct mac_iveiv_entry {
  *        increased by (agc_step * 4)
  * AGC_STEP: Temperature compensation step.
  */
-#define EEPROM_TSSI_BOUND_A5           0x006e
 #define EEPROM_TSSI_BOUND_A5_PLUS4     FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A5_AGC_STEP  FIELD16(0xff00)
 
 /*
  * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode
  */
-#define EEPROM_TXPOWER_BYRATE          0x006f
 #define EEPROM_TXPOWER_BYRATE_SIZE     9
 
 #define EEPROM_TXPOWER_BYRATE_RATE0    FIELD16(0x000f)
@@ -2529,11 +2710,14 @@ struct mac_iveiv_entry {
 /*
  * EEPROM BBP.
  */
-#define        EEPROM_BBP_START                0x0078
 #define EEPROM_BBP_SIZE                        16
 #define EEPROM_BBP_VALUE               FIELD16(0x00ff)
 #define EEPROM_BBP_REG_ID              FIELD16(0xff00)
 
+/* EEPROM_EXT_LNA2 */
+#define EEPROM_EXT_LNA2_A1             FIELD16(0x00ff)
+#define EEPROM_EXT_LNA2_A2             FIELD16(0xff00)
+
 /*
  * EEPROM IQ Calibration, unlike other entries those are byte addresses.
  */
@@ -2630,6 +2814,7 @@ struct mac_iveiv_entry {
 #define TXWI_DESC_SIZE_5WORDS          (5 * sizeof(__le32))
 
 #define RXWI_DESC_SIZE_4WORDS          (4 * sizeof(__le32))
+#define RXWI_DESC_SIZE_5WORDS          (5 * sizeof(__le32))
 #define RXWI_DESC_SIZE_6WORDS          (6 * sizeof(__le32))
 
 /*
@@ -2750,18 +2935,15 @@ struct mac_iveiv_entry {
 #define MAX_A_TXPOWER  15
 #define DEFAULT_TXPOWER        5
 
+#define MIN_A_TXPOWER_3593     0
+#define MAX_A_TXPOWER_3593     31
+
 #define TXPOWER_G_FROM_DEV(__txpower) \
        ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
 
-#define TXPOWER_G_TO_DEV(__txpower) \
-       clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER)
-
 #define TXPOWER_A_FROM_DEV(__txpower) \
        ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
 
-#define TXPOWER_A_TO_DEV(__txpower) \
-       clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER)
-
 /*
  *  Board's maximun TX power limitation
  */
index 1f80ea5..dedc3d4 100644 (file)
@@ -221,6 +221,157 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
        mutex_unlock(&rt2x00dev->csr_mutex);
 }
 
+static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
+       [EEPROM_CHIP_ID]                = 0x0000,
+       [EEPROM_VERSION]                = 0x0001,
+       [EEPROM_MAC_ADDR_0]             = 0x0002,
+       [EEPROM_MAC_ADDR_1]             = 0x0003,
+       [EEPROM_MAC_ADDR_2]             = 0x0004,
+       [EEPROM_NIC_CONF0]              = 0x001a,
+       [EEPROM_NIC_CONF1]              = 0x001b,
+       [EEPROM_FREQ]                   = 0x001d,
+       [EEPROM_LED_AG_CONF]            = 0x001e,
+       [EEPROM_LED_ACT_CONF]           = 0x001f,
+       [EEPROM_LED_POLARITY]           = 0x0020,
+       [EEPROM_NIC_CONF2]              = 0x0021,
+       [EEPROM_LNA]                    = 0x0022,
+       [EEPROM_RSSI_BG]                = 0x0023,
+       [EEPROM_RSSI_BG2]               = 0x0024,
+       [EEPROM_TXMIXER_GAIN_BG]        = 0x0024, /* overlaps with RSSI_BG2 */
+       [EEPROM_RSSI_A]                 = 0x0025,
+       [EEPROM_RSSI_A2]                = 0x0026,
+       [EEPROM_TXMIXER_GAIN_A]         = 0x0026, /* overlaps with RSSI_A2 */
+       [EEPROM_EIRP_MAX_TX_POWER]      = 0x0027,
+       [EEPROM_TXPOWER_DELTA]          = 0x0028,
+       [EEPROM_TXPOWER_BG1]            = 0x0029,
+       [EEPROM_TXPOWER_BG2]            = 0x0030,
+       [EEPROM_TSSI_BOUND_BG1]         = 0x0037,
+       [EEPROM_TSSI_BOUND_BG2]         = 0x0038,
+       [EEPROM_TSSI_BOUND_BG3]         = 0x0039,
+       [EEPROM_TSSI_BOUND_BG4]         = 0x003a,
+       [EEPROM_TSSI_BOUND_BG5]         = 0x003b,
+       [EEPROM_TXPOWER_A1]             = 0x003c,
+       [EEPROM_TXPOWER_A2]             = 0x0053,
+       [EEPROM_TSSI_BOUND_A1]          = 0x006a,
+       [EEPROM_TSSI_BOUND_A2]          = 0x006b,
+       [EEPROM_TSSI_BOUND_A3]          = 0x006c,
+       [EEPROM_TSSI_BOUND_A4]          = 0x006d,
+       [EEPROM_TSSI_BOUND_A5]          = 0x006e,
+       [EEPROM_TXPOWER_BYRATE]         = 0x006f,
+       [EEPROM_BBP_START]              = 0x0078,
+};
+
+static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
+       [EEPROM_CHIP_ID]                = 0x0000,
+       [EEPROM_VERSION]                = 0x0001,
+       [EEPROM_MAC_ADDR_0]             = 0x0002,
+       [EEPROM_MAC_ADDR_1]             = 0x0003,
+       [EEPROM_MAC_ADDR_2]             = 0x0004,
+       [EEPROM_NIC_CONF0]              = 0x001a,
+       [EEPROM_NIC_CONF1]              = 0x001b,
+       [EEPROM_NIC_CONF2]              = 0x001c,
+       [EEPROM_EIRP_MAX_TX_POWER]      = 0x0020,
+       [EEPROM_FREQ]                   = 0x0022,
+       [EEPROM_LED_AG_CONF]            = 0x0023,
+       [EEPROM_LED_ACT_CONF]           = 0x0024,
+       [EEPROM_LED_POLARITY]           = 0x0025,
+       [EEPROM_LNA]                    = 0x0026,
+       [EEPROM_EXT_LNA2]               = 0x0027,
+       [EEPROM_RSSI_BG]                = 0x0028,
+       [EEPROM_TXPOWER_DELTA]          = 0x0028, /* Overlaps with RSSI_BG */
+       [EEPROM_RSSI_BG2]               = 0x0029,
+       [EEPROM_TXMIXER_GAIN_BG]        = 0x0029, /* Overlaps with RSSI_BG2 */
+       [EEPROM_RSSI_A]                 = 0x002a,
+       [EEPROM_RSSI_A2]                = 0x002b,
+       [EEPROM_TXMIXER_GAIN_A]         = 0x002b, /* Overlaps with RSSI_A2 */
+       [EEPROM_TXPOWER_BG1]            = 0x0030,
+       [EEPROM_TXPOWER_BG2]            = 0x0037,
+       [EEPROM_EXT_TXPOWER_BG3]        = 0x003e,
+       [EEPROM_TSSI_BOUND_BG1]         = 0x0045,
+       [EEPROM_TSSI_BOUND_BG2]         = 0x0046,
+       [EEPROM_TSSI_BOUND_BG3]         = 0x0047,
+       [EEPROM_TSSI_BOUND_BG4]         = 0x0048,
+       [EEPROM_TSSI_BOUND_BG5]         = 0x0049,
+       [EEPROM_TXPOWER_A1]             = 0x004b,
+       [EEPROM_TXPOWER_A2]             = 0x0065,
+       [EEPROM_EXT_TXPOWER_A3]         = 0x007f,
+       [EEPROM_TSSI_BOUND_A1]          = 0x009a,
+       [EEPROM_TSSI_BOUND_A2]          = 0x009b,
+       [EEPROM_TSSI_BOUND_A3]          = 0x009c,
+       [EEPROM_TSSI_BOUND_A4]          = 0x009d,
+       [EEPROM_TSSI_BOUND_A5]          = 0x009e,
+       [EEPROM_TXPOWER_BYRATE]         = 0x00a0,
+};
+
+static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev,
+                                            const enum rt2800_eeprom_word word)
+{
+       const unsigned int *map;
+       unsigned int index;
+
+       if (WARN_ONCE(word >= EEPROM_WORD_COUNT,
+                     "%s: invalid EEPROM word %d\n",
+                     wiphy_name(rt2x00dev->hw->wiphy), word))
+               return 0;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               map = rt2800_eeprom_map_ext;
+       else
+               map = rt2800_eeprom_map;
+
+       index = map[word];
+
+       /* Index 0 is valid only for EEPROM_CHIP_ID.
+        * Otherwise it means that the offset of the
+        * given word is not initialized in the map,
+        * or that the field is not usable on the
+        * actual chipset.
+        */
+       WARN_ONCE(word != EEPROM_CHIP_ID && index == 0,
+                 "%s: invalid access of EEPROM word %d\n",
+                 wiphy_name(rt2x00dev->hw->wiphy), word);
+
+       return index;
+}
+
+static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev,
+                               const enum rt2800_eeprom_word word)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       return rt2x00_eeprom_addr(rt2x00dev, index);
+}
+
+static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev,
+                              const enum rt2800_eeprom_word word, u16 *data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       rt2x00_eeprom_read(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev,
+                               const enum rt2800_eeprom_word word, u16 data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       rt2x00_eeprom_write(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev,
+                                         const enum rt2800_eeprom_word array,
+                                         unsigned int offset,
+                                         u16 *data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, array);
+       rt2x00_eeprom_read(rt2x00dev, index + offset, data);
+}
+
 static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
@@ -609,16 +760,16 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2)
        u8 offset2;
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
                offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
                offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
                offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2);
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
                offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0);
                offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
                offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2);
        }
 
@@ -890,6 +1041,9 @@ const struct rt2x00debug rt2800_rt2x00debug = {
                .word_count     = CSR_REG_SIZE / sizeof(u32),
        },
        .eeprom = {
+               /* NOTE: The local EEPROM access functions can't
+                * be used here, use the generic versions instead.
+                */
                .read           = rt2x00_eeprom_read,
                .write          = rt2x00_eeprom_write,
                .word_base      = EEPROM_BASE,
@@ -1547,7 +1701,7 @@ static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev)
        led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3;
        if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) ||
            led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
                led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE);
                if (led_ctrl == 0 || led_ctrl > 0x40) {
                        rt2x00_set_field32(&reg, LED_CFG_G_LED_MODE, led_g_mode);
@@ -1609,7 +1763,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
                break;
        case 3:
-               rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
+               rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
                break;
        }
 
@@ -1622,7 +1776,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                    rt2x00_rt(rt2x00dev, RT3090) ||
                    rt2x00_rt(rt2x00dev, RT3352) ||
                    rt2x00_rt(rt2x00dev, RT3390)) {
-                       rt2x00_eeprom_read(rt2x00dev,
+                       rt2800_eeprom_read(rt2x00dev,
                                           EEPROM_NIC_CONF1, &eeprom);
                        if (rt2x00_get_field16(eeprom,
                                                EEPROM_NIC_CONF1_ANT_DIVERSITY))
@@ -1649,6 +1803,13 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
 
        rt2800_bbp_write(rt2x00dev, 3, r3);
        rt2800_bbp_write(rt2x00dev, 1, r1);
+
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (ant->rx_chain_num == 1)
+                       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+               else
+                       rt2800_bbp_write(rt2x00dev, 86, 0x46);
+       }
 }
 EXPORT_SYMBOL_GPL(rt2800_config_ant);
 
@@ -1659,17 +1820,31 @@ static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain;
 
        if (libconf->rf.channel <= 14) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
                lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG);
        } else if (libconf->rf.channel <= 64) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
                lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0);
        } else if (libconf->rf.channel <= 128) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
-               lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1);
+               if (rt2x00_rt(rt2x00dev, RT3593)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_EXT_LNA2_A1);
+               } else {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_RSSI_BG2_LNA_A1);
+               }
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
-               lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2);
+               if (rt2x00_rt(rt2x00dev, RT3593)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_EXT_LNA2_A2);
+               } else {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_RSSI_A2_LNA_A2);
+               }
        }
 
        rt2x00dev->lna_gain = lna_gain;
@@ -1993,6 +2168,303 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
        rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
 }
 
+static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_conf *conf,
+                                        struct rf_channel *rf,
+                                        struct channel_info *info)
+{
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u8 txrx_agc_fc;
+       u8 txrx_h20m;
+       u8 rfcsr;
+       u8 bbp;
+       const bool txbf_enabled = false; /* TODO */
+
+       /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */
+       rt2800_bbp_read(rt2x00dev, 109, &bbp);
+       rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0);
+       rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0);
+       rt2800_bbp_write(rt2x00dev, 109, bbp);
+
+       rt2800_bbp_read(rt2x00dev, 110, &bbp);
+       rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0);
+       rt2800_bbp_write(rt2x00dev, 110, bbp);
+
+       if (rf->channel <= 14) {
+               /* Restore BBP 25 & 26 for 2.4 GHz */
+               rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25);
+               rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26);
+       } else {
+               /* Hard code BBP 25 & 26 for 5GHz */
+
+               /* Enable IQ Phase correction */
+               rt2800_bbp_write(rt2x00dev, 25, 0x09);
+               /* Setup IQ Phase correction value */
+               rt2800_bbp_write(rt2x00dev, 26, 0xff);
+       }
+
+       rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1);
+       rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf);
+
+       rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3));
+       rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2);
+       rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+                                 info->default_power1 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+                                 ((info->default_power1 & 0x18) << 1) |
+                                 (info->default_power1 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 53, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+                                 info->default_power2 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+                                 ((info->default_power2 & 0x18) << 1) |
+                                 (info->default_power2 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 55, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+                                 info->default_power3 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+                                 ((info->default_power3 & 0x18) << 1) |
+                                 (info->default_power3 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 54, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+
+       switch (rt2x00dev->default_ant.tx_chain_num) {
+       case 3:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
+               /* fallthrough */
+       case 2:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+               /* fallthrough */
+       case 1:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
+               break;
+       }
+
+       switch (rt2x00dev->default_ant.rx_chain_num) {
+       case 3:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
+               /* fallthrough */
+       case 2:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+               /* fallthrough */
+       case 1:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
+               break;
+       }
+       rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+       /* TODO: frequency calibration? */
+
+       if (conf_is_ht40(conf)) {
+               txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40,
+                                               RFCSR24_TX_AGC_FC);
+               txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40,
+                                             RFCSR24_TX_H20M);
+       } else {
+               txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20,
+                                               RFCSR24_TX_AGC_FC);
+               txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20,
+                                             RFCSR24_TX_H20M);
+       }
+
+       /* NOTE: the reference driver does not writes the new value
+        * back to RFCSR 32
+        */
+       rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc);
+
+       if (rf->channel <= 14)
+               rfcsr = 0xa0;
+       else
+               rfcsr = 0x80;
+       rt2800_rfcsr_write(rt2x00dev, 31, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       /* Band selection */
+       rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0);
+       rt2800_rfcsr_write(rt2x00dev, 36, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr);
+       if (rf->channel <= 14)
+               rfcsr = 0x3c;
+       else
+               rfcsr = 0x20;
+       rt2800_rfcsr_write(rt2x00dev, 34, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
+       if (rf->channel <= 14)
+               rfcsr = 0x1a;
+       else
+               rfcsr = 0x12;
+       rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+       if (rf->channel >= 1 && rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+       else if (rf->channel >= 36 && rf->channel <= 64)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+       else if (rf->channel >= 100 && rf->channel <= 128)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+       rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+
+       if (rf->channel <= 14) {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+               rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+       } else {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0xd8);
+               rt2800_rfcsr_write(rt2x00dev, 13, 0x23);
+       }
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1);
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       if (rf->channel <= 14) {
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5);
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3);
+       } else {
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4);
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2);
+       }
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2);
+
+       if (txbf_enabled)
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1);
+
+       rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 57, rfcsr);
+
+       if (rf->channel <= 14) {
+               rt2800_rfcsr_write(rt2x00dev, 44, 0x93);
+               rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+       } else {
+               rt2800_rfcsr_write(rt2x00dev, 44, 0x9b);
+               rt2800_rfcsr_write(rt2x00dev, 52, 0x05);
+       }
+
+       /* Initiate VCO calibration */
+       rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
+       if (rf->channel <= 14) {
+               rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+       } else {
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+       }
+       rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
+
+       if (rf->channel >= 1 && rf->channel <= 14) {
+               rfcsr = 0x23;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+       } else if (rf->channel >= 36 && rf->channel <= 64) {
+               rfcsr = 0x36;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, 0x36);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xeb);
+       } else if (rf->channel >= 100 && rf->channel <= 128) {
+               rfcsr = 0x32;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xb3);
+       } else {
+               rfcsr = 0x30;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0x9b);
+       }
+}
+
 #define POWER_BOUND            0x27
 #define POWER_BOUND_5G         0x2b
 #define FREQ_OFFSET_BOUND      0x5f
@@ -2563,6 +3035,23 @@ static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel)
        rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0);
 }
 
+static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev,
+                                 unsigned int channel,
+                                 char txpower)
+{
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC);
+
+       if (channel <= 14)
+               return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER);
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return clamp_t(char, txpower, MIN_A_TXPOWER_3593,
+                              MAX_A_TXPOWER_3593);
+       else
+               return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER);
+}
+
 static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                                  struct ieee80211_conf *conf,
                                  struct rf_channel *rf,
@@ -2572,13 +3061,14 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        unsigned int tx_pin;
        u8 bbp, rfcsr;
 
-       if (rf->channel <= 14) {
-               info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
-               info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
-       } else {
-               info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
-               info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
-       }
+       info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                                    info->default_power1);
+       info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                                    info->default_power2);
+       if (rt2x00dev->default_ant.tx_chain_num > 2)
+               info->default_power3 =
+                       rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                             info->default_power3);
 
        switch (rt2x00dev->chip.rf) {
        case RF2020:
@@ -2591,6 +3081,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        case RF3052:
                rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info);
                break;
+       case RF3053:
+               rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info);
+               break;
        case RF3290:
                rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info);
                break;
@@ -2636,6 +3129,23 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
                rt2800_bbp_write(rt2x00dev, 27, 0x20);
                rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
+       } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rf->channel > 14) {
+                       /* Disable CCK Packet detection on 5GHz */
+                       rt2800_bbp_write(rt2x00dev, 70, 0x00);
+               } else {
+                       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+               }
+
+               if (conf_is_ht40(conf))
+                       rt2800_bbp_write(rt2x00dev, 105, 0x04);
+               else
+                       rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+               rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 77, 0x98);
        } else {
                rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
                rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
@@ -2651,16 +3161,27 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                                rt2800_bbp_write(rt2x00dev, 82, 0x62);
                                rt2800_bbp_write(rt2x00dev, 75, 0x46);
                        } else {
-                               rt2800_bbp_write(rt2x00dev, 82, 0x84);
+                               if (rt2x00_rt(rt2x00dev, RT3593))
+                                       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+                               else
+                                       rt2800_bbp_write(rt2x00dev, 82, 0x84);
                                rt2800_bbp_write(rt2x00dev, 75, 0x50);
                        }
+                       if (rt2x00_rt(rt2x00dev, RT3593))
+                               rt2800_bbp_write(rt2x00dev, 83, 0x8a);
                }
+
        } else {
                if (rt2x00_rt(rt2x00dev, RT3572))
                        rt2800_bbp_write(rt2x00dev, 82, 0x94);
+               else if (rt2x00_rt(rt2x00dev, RT3593))
+                       rt2800_bbp_write(rt2x00dev, 82, 0x82);
                else
                        rt2800_bbp_write(rt2x00dev, 82, 0xf2);
 
+               if (rt2x00_rt(rt2x00dev, RT3593))
+                       rt2800_bbp_write(rt2x00dev, 83, 0x9a);
+
                if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
                        rt2800_bbp_write(rt2x00dev, 75, 0x46);
                else
@@ -2731,6 +3252,41 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        if (rt2x00_rt(rt2x00dev, RT3572))
                rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
 
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_is_usb(rt2x00dev)) {
+                       rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+
+                       /* Band selection. GPIO #8 controls all paths */
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR8, 0);
+                       if (rf->channel <= 14)
+                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 1);
+                       else
+                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 0);
+
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR4, 0);
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR7, 0);
+
+                       /* LNA PE control.
+                       * GPIO #4 controls PE0 and PE1,
+                       * GPIO #7 controls PE2
+                       */
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL4, 1);
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL7, 1);
+
+                       rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
+               }
+
+               /* AGC init */
+               if (rf->channel <= 14)
+                       reg = 0x1c + 2 * rt2x00dev->lna_gain;
+               else
+                       reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3);
+
+               rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
+
+               usleep_range(1000, 1500);
+       }
+
        if (rt2x00_rt(rt2x00dev, RT5592)) {
                rt2800_bbp_write(rt2x00dev, 195, 141);
                rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a);
@@ -2798,62 +3354,62 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev)
         * Example TSSI bounds  0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00
         */
        if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
                tssi_bounds[0] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG1_MINUS4);
                tssi_bounds[1] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG1_MINUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
                tssi_bounds[2] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG2_MINUS2);
                tssi_bounds[3] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG2_MINUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
                tssi_bounds[4] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG3_REF);
                tssi_bounds[5] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG3_PLUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
                tssi_bounds[6] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG4_PLUS2);
                tssi_bounds[7] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG4_PLUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
                tssi_bounds[8] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG5_PLUS4);
 
                step = rt2x00_get_field16(eeprom,
                                          EEPROM_TSSI_BOUND_BG5_AGC_STEP);
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
                tssi_bounds[0] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A1_MINUS4);
                tssi_bounds[1] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A1_MINUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
                tssi_bounds[2] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A2_MINUS2);
                tssi_bounds[3] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A2_MINUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
                tssi_bounds[4] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A3_REF);
                tssi_bounds[5] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A3_PLUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
                tssi_bounds[6] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A4_PLUS2);
                tssi_bounds[7] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A4_PLUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
                tssi_bounds[8] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A5_PLUS4);
 
@@ -2899,7 +3455,7 @@ static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev,
        u8 comp_type;
        int comp_value = 0;
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
 
        /*
         * HT40 compensation not required.
@@ -2966,6 +3522,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        u8 eirp_txpower_criterion;
        u8 reg_limit;
 
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return min_t(u8, txpower, 0xc);
+
        if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
                /*
                 * Check if eirp txpower exceed txpower_limit.
@@ -2974,12 +3533,12 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
                 * .11b data rate need add additional 4dbm
                 * when calculating eirp txpower.
                 */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + 1,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             1, &eeprom);
                criterion = rt2x00_get_field16(eeprom,
                                               EEPROM_TXPOWER_BYRATE_RATE0);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
+               rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
                                   &eeprom);
 
                if (band == IEEE80211_BAND_2GHZ)
@@ -3001,6 +3560,412 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        return min_t(u8, txpower, 0xc);
 }
 
+
+enum {
+       TX_PWR_CFG_0_IDX,
+       TX_PWR_CFG_1_IDX,
+       TX_PWR_CFG_2_IDX,
+       TX_PWR_CFG_3_IDX,
+       TX_PWR_CFG_4_IDX,
+       TX_PWR_CFG_5_IDX,
+       TX_PWR_CFG_6_IDX,
+       TX_PWR_CFG_7_IDX,
+       TX_PWR_CFG_8_IDX,
+       TX_PWR_CFG_9_IDX,
+       TX_PWR_CFG_0_EXT_IDX,
+       TX_PWR_CFG_1_EXT_IDX,
+       TX_PWR_CFG_2_EXT_IDX,
+       TX_PWR_CFG_3_EXT_IDX,
+       TX_PWR_CFG_4_EXT_IDX,
+       TX_PWR_CFG_IDX_COUNT,
+};
+
+static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_channel *chan,
+                                        int power_level)
+{
+       u8 txpower;
+       u16 eeprom;
+       u32 regs[TX_PWR_CFG_IDX_COUNT];
+       unsigned int offset;
+       enum ieee80211_band band = chan->band;
+       int delta;
+       int i;
+
+       memset(regs, '\0', sizeof(regs));
+
+       /* TODO: adapt TX power reduction from the rt28xx code */
+
+       /* calculate temperature compensation delta */
+       delta = rt2800_get_gain_calibration_delta(rt2x00dev);
+
+       if (band == IEEE80211_BAND_5GHZ)
+               offset = 16;
+       else
+               offset = 0;
+
+       if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
+               offset += 8;
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset, &eeprom);
+
+       /* CCK 1MBS,2MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK1_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK1_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_CCK1_CH2, txpower);
+
+       /* CCK 5.5MBS,11MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK5_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK5_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_CCK5_CH2, txpower);
+
+       /* OFDM 6MBS,9MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM6_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM6_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower);
+
+       /* OFDM 12MBS,18MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM12_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM12_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 1, &eeprom);
+
+       /* OFDM 24MBS,36MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM24_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM24_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower);
+
+       /* OFDM 48MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM48_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM48_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower);
+
+       /* OFDM 54MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 2, &eeprom);
+
+       /* MCS 0,1 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS0_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS0_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_MCS0_CH2, txpower);
+
+       /* MCS 2,3 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS2_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS2_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_MCS2_CH2, txpower);
+
+       /* MCS 4,5 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS4_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS4_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS4_CH2, txpower);
+
+       /* MCS 6 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS6_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS6_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS6_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 3, &eeprom);
+
+       /* MCS 7 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH2, txpower);
+
+       /* MCS 8,9 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS8_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS8_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS8_CH2, txpower);
+
+       /* MCS 10,11 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS10_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS10_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS10_CH2, txpower);
+
+       /* MCS 12,13 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS12_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS12_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_MCS12_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 4, &eeprom);
+
+       /* MCS 14 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS14_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS14_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_MCS14_CH2, txpower);
+
+       /* MCS 15 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH2, txpower);
+
+       /* MCS 16,17 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH2, txpower);
+
+       /* MCS 18,19 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 5, &eeprom);
+
+       /* MCS 20,21 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH2, txpower);
+
+       /* MCS 22 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH2, txpower);
+
+       /* MCS 23 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 6, &eeprom);
+
+       /* STBC, MCS 0,1 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC0_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC0_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_STBC0_CH2, txpower);
+
+       /* STBC, MCS 2,3 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC2_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC2_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_STBC2_CH2, txpower);
+
+       /* STBC, MCS 4,5 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0,
+                          txpower);
+
+       /* STBC, MCS 6 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2,
+                          txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 7, &eeprom);
+
+       /* STBC, MCS 7 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH2, txpower);
+
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]);
+
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT,
+                             regs[TX_PWR_CFG_0_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT,
+                             regs[TX_PWR_CFG_1_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT,
+                             regs[TX_PWR_CFG_2_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT,
+                             regs[TX_PWR_CFG_3_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT,
+                             regs[TX_PWR_CFG_4_EXT_IDX]);
+
+       for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++)
+               rt2x00_dbg(rt2x00dev,
+                          "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n",
+                          (band == IEEE80211_BAND_5GHZ) ? '5' : '2',
+                          (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ?
+                                                               '4' : '2',
+                          (i > TX_PWR_CFG_9_IDX) ?
+                                       (i - TX_PWR_CFG_9_IDX - 1) : i,
+                          (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "",
+                          (unsigned long) regs[i]);
+}
+
 /*
  * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and
  * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values,
@@ -3010,9 +3975,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
  * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to
  * current conditions (i.e. band, bandwidth, temperature, user settings).
  */
-static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
-                                 struct ieee80211_channel *chan,
-                                 int power_level)
+static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_channel *chan,
+                                        int power_level)
 {
        u8 txpower, r1;
        u16 eeprom;
@@ -3080,8 +4045,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
                rt2800_register_read(rt2x00dev, offset, &reg);
 
                /* read the next four txpower values */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             i, &eeprom);
 
                is_rate_b = i ? 0 : 1;
                /*
@@ -3129,8 +4094,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
                rt2x00_set_field32(&reg, TX_PWR_CFG_RATE3, txpower);
 
                /* read the next four txpower values */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             i + 1, &eeprom);
 
                is_rate_b = 0;
                /*
@@ -3184,6 +4149,16 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
        }
 }
 
+static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
+                                 struct ieee80211_channel *chan,
+                                 int power_level)
+{
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level);
+       else
+               rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level);
+}
+
 void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev)
 {
        rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan,
@@ -3219,6 +4194,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
                rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
                break;
+       case RF3053:
        case RF3290:
        case RF5360:
        case RF5370:
@@ -3528,7 +4504,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
-                       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
                        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
                                rt2800_register_write(rt2x00dev, TX_SW_CFG2,
                                                      0x0000002c);
@@ -3559,6 +4536,23 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        } else if (rt2x00_rt(rt2x00dev, RT3572)) {
                rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
                rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
+       } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+               rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402);
+               rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
+               if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
+                       if (rt2x00_get_field16(eeprom,
+                                              EEPROM_NIC_CONF1_DAC_TEST))
+                               rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                                     0x0000001f);
+                       else
+                               rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                                     0x0000000f);
+               } else {
+                       rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                             0x00000000);
+               }
        } else if (rt2x00_rt(rt2x00dev, RT5390) ||
                   rt2x00_rt(rt2x00dev, RT5392) ||
                   rt2x00_rt(rt2x00dev, RT5592)) {
@@ -3989,7 +4983,7 @@ static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
        u8 value;
 
        rt2800_bbp_read(rt2x00dev, 138, &value);
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
                value |= 0x20;
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
@@ -4332,6 +5326,22 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
        rt2800_disable_unused_dac_adc(rt2x00dev);
 }
 
+static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_init_bbp_early(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
+       rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x19);
+
+       /* Enable DC filter */
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
+
 static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
 {
        int ant, div_mode;
@@ -4402,7 +5412,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
 
        rt2800_disable_unused_dac_adc(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
        div_mode = rt2x00_get_field16(eeprom,
                                      EEPROM_NIC_CONF1_ANT_DIVERSITY);
        ant = (div_mode == 3) ? 1 : 0;
@@ -4488,7 +5498,7 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
 
        rt2800_bbp4_mac_if_ctrl(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
        div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
        ant = (div_mode == 3) ? 1 : 0;
        rt2800_bbp_read(rt2x00dev, 152, &value);
@@ -4547,6 +5557,9 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
        case RT3572:
                rt2800_init_bbp_3572(rt2x00dev);
                break;
+       case RT3593:
+               rt2800_init_bbp_3593(rt2x00dev);
+               return;
        case RT5390:
        case RT5392:
                rt2800_init_bbp_53xx(rt2x00dev);
@@ -4557,7 +5570,8 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
        }
 
        for (i = 0; i < EEPROM_BBP_SIZE; i++) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i,
+                                             &eeprom);
 
                if (eeprom != 0xffff && eeprom != 0x0000) {
                        reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
@@ -4728,7 +5742,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
        if (rt2x00_rt(rt2x00dev, RT3090)) {
                /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
                rt2800_bbp_read(rt2x00dev, 138, &bbp);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
                if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
                        rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0);
                if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
@@ -4771,6 +5785,42 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
        }
 }
 
+static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev)
+{
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u8 rfcsr;
+       u8 tx_gain;
+
+       rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g,
+                                   RFCSR17_TXMIXER_GAIN);
+       rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain);
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 38, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+       rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       /* TODO: enable stream mode */
+}
+
 static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev)
 {
        u8 reg;
@@ -4778,7 +5828,7 @@ static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev)
 
        /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
        rt2800_bbp_read(rt2x00dev, 138, &reg);
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
                rt2x00_set_field8(&reg, BBP138_RX_ADC1, 0);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
@@ -4884,7 +5934,8 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
                if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) {
-                       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
                        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
                                rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 3);
                        else
@@ -5152,6 +6203,136 @@ static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev)
        rt2800_normal_mode_setup_3xxx(rt2x00dev);
 }
 
+static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev)
+{
+       u8 bbp;
+       bool txbf_enabled = false; /* FIXME */
+
+       rt2800_bbp_read(rt2x00dev, 105, &bbp);
+       if (rt2x00dev->default_ant.rx_chain_num == 1)
+               rt2x00_set_field8(&bbp, BBP105_MLD, 0);
+       else
+               rt2x00_set_field8(&bbp, BBP105_MLD, 1);
+       rt2800_bbp_write(rt2x00dev, 105, bbp);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
+       rt2800_bbp_write(rt2x00dev, 82, 0x82);
+       rt2800_bbp_write(rt2x00dev, 106, 0x05);
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
+       rt2800_bbp_write(rt2x00dev, 88, 0x90);
+       rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+       rt2800_bbp_write(rt2x00dev, 47, 0x48);
+       rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+       if (txbf_enabled)
+               rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+       else
+               rt2800_bbp_write(rt2x00dev, 163, 0x9d);
+
+       /* SNR mapping */
+       rt2800_bbp_write(rt2x00dev, 142, 6);
+       rt2800_bbp_write(rt2x00dev, 143, 160);
+       rt2800_bbp_write(rt2x00dev, 142, 7);
+       rt2800_bbp_write(rt2x00dev, 143, 161);
+       rt2800_bbp_write(rt2x00dev, 142, 8);
+       rt2800_bbp_write(rt2x00dev, 143, 162);
+
+       /* ADC/DAC control */
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+       /* RX AGC energy lower bound in log2 */
+       rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+       /* FIXME: BBP 105 owerwrite? */
+       rt2800_bbp_write(rt2x00dev, 105, 0x04);
+
+}
+
+static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev)
+{
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u32 reg;
+       u8 rfcsr;
+
+       /* Disable GPIO #4 and #7 function for LAN PE control */
+       rt2800_register_read(rt2x00dev, GPIO_SWITCH, &reg);
+       rt2x00_set_field32(&reg, GPIO_SWITCH_4, 0);
+       rt2x00_set_field32(&reg, GPIO_SWITCH_7, 0);
+       rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg);
+
+       /* Initialize default register values */
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x4e);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x78);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0x3b);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x3c);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0xe0);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x86);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0x23);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0xd3);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x8e);
+       rt2800_rfcsr_write(rt2x00dev, 50, 0x86);
+       rt2800_rfcsr_write(rt2x00dev, 51, 0x75);
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x6e);
+
+       /* Initiate calibration */
+       /* TODO: use rt2800_rf_init_calibration ? */
+       rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1);
+       rt2800_rfcsr_write(rt2x00dev, 2, rfcsr);
+
+       rt2800_adjust_freq_offset(rt2x00dev);
+
+       rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1);
+       rt2800_rfcsr_write(rt2x00dev, 18, rfcsr);
+
+       rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+       rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 3);
+       rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
+       rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+       usleep_range(1000, 1500);
+       rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+       rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 0);
+       rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+
+       /* Set initial values for RX filter calibration */
+       drv_data->calibration_bw20 = 0x1f;
+       drv_data->calibration_bw40 = 0x2f;
+
+       /* Save BBP 25 & 26 values for later use in channel switching */
+       rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25);
+       rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26);
+
+       rt2800_led_open_drain_enable(rt2x00dev);
+       rt2800_normal_mode_setup_3593(rt2x00dev);
+
+       rt3593_post_bbp_init(rt2x00dev);
+
+       /* TODO: enable stream mode support */
+}
+
 static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
 {
        rt2800_rf_init_calibration(rt2x00dev, 2);
@@ -5380,6 +6561,9 @@ static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        case RT3572:
                rt2800_init_rfcsr_3572(rt2x00dev);
                break;
+       case RT3593:
+               rt2800_init_rfcsr_3593(rt2x00dev);
+               break;
        case RT5390:
                rt2800_init_rfcsr_5390(rt2x00dev);
                break;
@@ -5456,15 +6640,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
        /*
         * Initialize LED control
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
@@ -5560,6 +6744,34 @@ int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
 
+static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev)
+{
+       u16 word;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return 0;
+
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
+       if ((word & 0x00ff) != 0x00ff)
+               return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
+
+       return 0;
+}
+
+static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev)
+{
+       u16 word;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return 0;
+
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
+       if ((word & 0x00ff) != 0x00ff)
+               return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
+
+       return 0;
+}
+
 static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
@@ -5578,18 +6790,18 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Start validation of the data that has been read.
         */
-       mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
+       mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
        if (!is_valid_ether_addr(mac)) {
                eth_random_addr(mac);
                rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
        if (word == 0xffff) {
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
                rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word);
        } else if (rt2x00_rt(rt2x00dev, RT2860) ||
                   rt2x00_rt(rt2x00dev, RT2872)) {
@@ -5598,10 +6810,10 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                 */
                if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2)
                        rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
        if (word == 0xffff) {
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0);
@@ -5618,24 +6830,24 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
                rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
        if ((word & 0x00ff) == 0x00ff) {
                rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
                rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word);
        }
        if ((word & 0xff00) == 0xff00) {
                rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE,
                                   LED_MODE_TXRX_ACTIVITY);
                rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
                rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word);
        }
 
@@ -5644,56 +6856,61 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
         * lna0 as correct value. Note that EEPROM_LNA
         * is never validated.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
        default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
-       if ((word & 0x00ff) != 0x00ff) {
-               drv_data->txmixer_gain_24g =
-                       rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
-       } else {
-               drv_data->txmixer_gain_24g = 0;
-       }
+       drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0);
-       if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
-           rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
-               rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
-                                  default_lna_gain);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
-
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
-       if ((word & 0x00ff) != 0x00ff) {
-               drv_data->txmixer_gain_5g =
-                       rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
-       } else {
-               drv_data->txmixer_gain_5g = 0;
+       if (!rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
+                                          default_lna_gain);
        }
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
+
+       drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0);
-       if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
-           rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
-               rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
-                                  default_lna_gain);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+       if (!rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
+                                          default_lna_gain);
+       }
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word);
+               if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+                                          default_lna_gain);
+               if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+                                          default_lna_gain);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word);
+       }
 
        return 0;
 }
@@ -5707,7 +6924,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Read EEPROM word for configuration.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
 
        /*
         * Identify RF chipset by EEPROM value
@@ -5717,7 +6934,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        if (rt2x00_rt(rt2x00dev, RT3290) ||
            rt2x00_rt(rt2x00dev, RT5390) ||
            rt2x00_rt(rt2x00dev, RT5392))
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
        else
                rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
 
@@ -5731,6 +6948,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        case RF3021:
        case RF3022:
        case RF3052:
+       case RF3053:
        case RF3290:
        case RF3320:
        case RF3322:
@@ -5757,7 +6975,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->default_ant.rx_chain_num =
            rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
 
        if (rt2x00_rt(rt2x00dev, RT3070) ||
            rt2x00_rt(rt2x00dev, RT3090) ||
@@ -5810,7 +7028,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Read frequency offset and RF programming sequence.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
        rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET);
 
        /*
@@ -5827,7 +7045,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Check if support EIRP tx power limit feature.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
 
        if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) <
                                        EIRP_MAX_TX_POWER_LIMIT)
@@ -6109,12 +7327,79 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
        {196, 83, 0, 12, 1},
 };
 
+static const struct rf_channel rf_vals_3053[] = {
+       /* Channel, N, R, K */
+       {1, 241, 2, 2},
+       {2, 241, 2, 7},
+       {3, 242, 2, 2},
+       {4, 242, 2, 7},
+       {5, 243, 2, 2},
+       {6, 243, 2, 7},
+       {7, 244, 2, 2},
+       {8, 244, 2, 7},
+       {9, 245, 2, 2},
+       {10, 245, 2, 7},
+       {11, 246, 2, 2},
+       {12, 246, 2, 7},
+       {13, 247, 2, 2},
+       {14, 248, 2, 4},
+
+       {36, 0x56, 0, 4},
+       {38, 0x56, 0, 6},
+       {40, 0x56, 0, 8},
+       {44, 0x57, 0, 0},
+       {46, 0x57, 0, 2},
+       {48, 0x57, 0, 4},
+       {52, 0x57, 0, 8},
+       {54, 0x57, 0, 10},
+       {56, 0x58, 0, 0},
+       {60, 0x58, 0, 4},
+       {62, 0x58, 0, 6},
+       {64, 0x58, 0, 8},
+
+       {100, 0x5B, 0, 8},
+       {102, 0x5B, 0, 10},
+       {104, 0x5C, 0, 0},
+       {108, 0x5C, 0, 4},
+       {110, 0x5C, 0, 6},
+       {112, 0x5C, 0, 8},
+
+       /* NOTE: Channel 114 has been removed intentionally.
+        * The EEPROM contains no TX power values for that,
+        * and it is disabled in the vendor driver as well.
+        */
+
+       {116, 0x5D, 0, 0},
+       {118, 0x5D, 0, 2},
+       {120, 0x5D, 0, 4},
+       {124, 0x5D, 0, 8},
+       {126, 0x5D, 0, 10},
+       {128, 0x5E, 0, 0},
+       {132, 0x5E, 0, 4},
+       {134, 0x5E, 0, 6},
+       {136, 0x5E, 0, 8},
+       {140, 0x5F, 0, 0},
+
+       {149, 0x5F, 0, 9},
+       {151, 0x5F, 0, 11},
+       {153, 0x60, 0, 1},
+       {157, 0x60, 0, 5},
+       {159, 0x60, 0, 7},
+       {161, 0x60, 0, 9},
+       {165, 0x61, 0, 1},
+       {167, 0x61, 0, 3},
+       {169, 0x61, 0, 5},
+       {171, 0x61, 0, 7},
+       {173, 0x61, 0, 9},
+};
+
 static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
        struct channel_info *info;
        char *default_power1;
        char *default_power2;
+       char *default_power3;
        unsigned int i;
        u16 eeprom;
        u32 reg;
@@ -6148,7 +7433,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
        SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
-                               rt2x00_eeprom_addr(rt2x00dev,
+                               rt2800_eeprom_addr(rt2x00dev,
                                                   EEPROM_MAC_ADDR_0));
 
        /*
@@ -6164,7 +7449,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->max_report_rates = 7;
        rt2x00dev->hw->max_rate_tries = 1;
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
 
        /*
         * Initialize hw_mode information.
@@ -6199,6 +7484,10 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_3x);
                spec->channels = rf_vals_3x;
+       } else if (rt2x00_rf(rt2x00dev, RF3053)) {
+               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               spec->num_channels = ARRAY_SIZE(rf_vals_3053);
+               spec->channels = rf_vals_3053;
        } else if (rt2x00_rf(rt2x00dev, RF5592)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
 
@@ -6264,21 +7553,40 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 
        spec->channels_info = info;
 
-       default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
-       default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+       default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+       default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+
+       if (rt2x00dev->default_ant.tx_chain_num > 2)
+               default_power3 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_EXT_TXPOWER_BG3);
+       else
+               default_power3 = NULL;
 
        for (i = 0; i < 14; i++) {
                info[i].default_power1 = default_power1[i];
                info[i].default_power2 = default_power2[i];
+               if (default_power3)
+                       info[i].default_power3 = default_power3[i];
        }
 
        if (spec->num_channels > 14) {
-               default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
-               default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+               default_power1 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_TXPOWER_A1);
+               default_power2 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_TXPOWER_A2);
+
+               if (rt2x00dev->default_ant.tx_chain_num > 2)
+                       default_power3 =
+                               rt2800_eeprom_addr(rt2x00dev,
+                                                  EEPROM_EXT_TXPOWER_A3);
+               else
+                       default_power3 = NULL;
 
                for (i = 14; i < spec->num_channels; i++) {
                        info[i].default_power1 = default_power1[i - 14];
                        info[i].default_power2 = default_power2[i - 14];
+                       if (default_power3)
+                               info[i].default_power3 = default_power3[i - 14];
                }
        }
 
@@ -6289,6 +7597,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        case RF3022:
        case RF3320:
        case RF3052:
+       case RF3053:
        case RF3290:
        case RF5360:
        case RF5370:
@@ -6327,6 +7636,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
        case RT3352:
        case RT3390:
        case RT3572:
+       case RT3593:
        case RT5390:
        case RT5392:
        case RT5592:
index 840833b..fc9efdf 100644 (file)
@@ -854,7 +854,10 @@ static void rt2800usb_queue_init(struct data_queue *queue)
        struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
        unsigned short txwi_size, rxwi_size;
 
-       if (rt2x00_rt(rt2x00dev, RT5592)) {
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               txwi_size = TXWI_DESC_SIZE_4WORDS;
+               rxwi_size = RXWI_DESC_SIZE_5WORDS;
+       } else if (rt2x00_rt(rt2x00dev, RT5592)) {
                txwi_size = TXWI_DESC_SIZE_5WORDS;
                rxwi_size = RXWI_DESC_SIZE_6WORDS;
        } else {
@@ -1194,6 +1197,40 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Zinwell */
        { USB_DEVICE(0x5a57, 0x0284) },
 #endif
+#ifdef CONFIG_RT2800USB_RT3573
+       /* AirLive */
+       { USB_DEVICE(0x1b75, 0x7733) },
+       /* ASUS */
+       { USB_DEVICE(0x0b05, 0x17bc) },
+       { USB_DEVICE(0x0b05, 0x17ad) },
+       /* Belkin */
+       { USB_DEVICE(0x050d, 0x1103) },
+       /* Cameo */
+       { USB_DEVICE(0x148f, 0xf301) },
+       /* Edimax */
+       { USB_DEVICE(0x7392, 0x7733) },
+       /* Hawking */
+       { USB_DEVICE(0x0e66, 0x0020) },
+       { USB_DEVICE(0x0e66, 0x0021) },
+       /* I-O DATA */
+       { USB_DEVICE(0x04bb, 0x094e) },
+       /* Linksys */
+       { USB_DEVICE(0x13b1, 0x003b) },
+       /* Logitec */
+       { USB_DEVICE(0x0789, 0x016b) },
+       /* NETGEAR */
+       { USB_DEVICE(0x0846, 0x9012) },
+       { USB_DEVICE(0x0846, 0x9019) },
+       /* Planex */
+       { USB_DEVICE(0x2019, 0xed19) },
+       /* Ralink */
+       { USB_DEVICE(0x148f, 0x3573) },
+       /* Sitecom */
+       { USB_DEVICE(0x0df6, 0x0067) },
+       { USB_DEVICE(0x0df6, 0x006a) },
+       /* ZyXEL */
+       { USB_DEVICE(0x0586, 0x3421) },
+#endif
 #ifdef CONFIG_RT2800USB_RT53XX
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
index ee3fc57..fe4c572 100644 (file)
@@ -211,6 +211,7 @@ struct channel_info {
        short max_power;
        short default_power1;
        short default_power2;
+       short default_power3;
 };
 
 /*
index f9f059d..a98acef 100644 (file)
@@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv,
 
 static void rtl_rate_init(void *ppriv,
                          struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta)
 {
 }
index 8e3ec1e..0f7812e 100644 (file)
@@ -109,5 +109,8 @@ void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
                         u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
 bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw);
+void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
+                                struct ieee80211_sta *sta,
+                                u8 rssi_level);
 
 #endif
index 262e1e4..a1310ab 100644 (file)
@@ -49,8 +49,5 @@ bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw,
 u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw,
                            enum radio_path rfpath, u32 regaddr, u32 bitmask);
 void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
-void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
-                                struct ieee80211_sta *sta,
-                                u8 rssi_level);
 
 #endif
index b8db55c..d1b19c3 100644 (file)
@@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
 
 #ifdef CONFIG_PM
 static int
-wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
+wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
 {
        int num_fields = 0, in_field = 0, fields_size = 0;
        int i, pattern_len = 0;
@@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
  * Allocates an RX filter returned through f
  * which needs to be freed using rx_filter_free()
  */
-static int wl1271_convert_wowlan_pattern_to_rx_filter(
-       struct cfg80211_wowlan_trig_pkt_pattern *p,
-       struct wl12xx_rx_filter **f)
+static int
+wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
+                                          struct wl12xx_rx_filter **f)
 {
        int i, j, ret = 0;
        struct wl12xx_rx_filter *filter;
@@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl,
 
        /* Translate WoWLAN patterns into filters */
        for (i = 0; i < wow->n_patterns; i++) {
-               struct cfg80211_wowlan_trig_pkt_pattern *p;
+               struct cfg80211_pkt_pattern *p;
                struct wl12xx_rx_filter *filter = NULL;
 
                p = &wow->patterns[i];
index 36171fd..2cd9b0e 100644 (file)
@@ -138,7 +138,7 @@ config SSB_DRIVER_MIPS
 
 config SSB_SFLASH
        bool "SSB serial flash support"
-       depends on SSB_DRIVER_MIPS && BROKEN
+       depends on SSB_DRIVER_MIPS
        default y
 
 # Assumption: We are on embedded, if we compile the MIPS core.
index e84cf04..50328de 100644 (file)
@@ -151,8 +151,8 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
        sflash->size = sflash->blocksize * sflash->numblocks;
        sflash->present = true;
 
-       pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
-               e->name, e->blocksize, e->numblocks);
+       pr_info("Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
+               e->name, sflash->size / 1024, e->blocksize, e->numblocks);
 
        /* Prepare platform device, but don't register it yet. It's too early,
         * malloc (required by device_private_init) is not available yet. */
@@ -160,7 +160,5 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
                                         sflash->size;
        ssb_sflash_dev.dev.platform_data = sflash;
 
-       pr_err("Serial flash support is not implemented yet!\n");
-
-       return -ENOTSUPP;
+       return 0;
 }
index 622fc50..4d043c3 100644 (file)
@@ -72,7 +72,19 @@ struct bcma_host_ops {
 /* Core-ID values. */
 #define BCMA_CORE_OOB_ROUTER           0x367   /* Out of band */
 #define BCMA_CORE_4706_CHIPCOMMON      0x500
+#define BCMA_CORE_PCIEG2               0x501
+#define BCMA_CORE_DMA                  0x502
+#define BCMA_CORE_SDIO3                        0x503
+#define BCMA_CORE_USB20                        0x504
+#define BCMA_CORE_USB30                        0x505
+#define BCMA_CORE_A9JTAG               0x506
+#define BCMA_CORE_DDR23                        0x507
+#define BCMA_CORE_ROM                  0x508
+#define BCMA_CORE_NAND                 0x509
+#define BCMA_CORE_QSPI                 0x50A
+#define BCMA_CORE_CHIPCOMMON_B         0x50B
 #define BCMA_CORE_4706_SOC_RAM         0x50E
+#define BCMA_CORE_ARMCA9               0x510
 #define BCMA_CORE_4706_MAC_GBIT                0x52D
 #define BCMA_CORE_AMEMC                        0x52E   /* DDR1/2 memory controller core */
 #define BCMA_CORE_ALTA                 0x534   /* I2S core */
@@ -177,6 +189,11 @@ struct bcma_host_ops {
 #define  BCMA_PKG_ID_BCM5357   11
 #define BCMA_CHIP_ID_BCM53572  53572
 #define  BCMA_PKG_ID_BCM47188  9
+#define BCMA_CHIP_ID_BCM4707   53010
+#define  BCMA_PKG_ID_BCM4707   1
+#define  BCMA_PKG_ID_BCM4708   2
+#define  BCMA_PKG_ID_BCM4709   0
+#define BCMA_CHIP_ID_BCM53018  53018
 
 /* Board types (on PCI usually equals to the subsystem dev id) */
 /* BCM4313 */
index 7b0730a..aeaf6df 100644 (file)
@@ -461,6 +461,33 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
 }
 
 /**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return min(chandef->chan->max_reg_power - 6,
+                          chandef->chan->max_power);
+       case NL80211_CHAN_WIDTH_10:
+               return min(chandef->chan->max_reg_power - 3,
+                          chandef->chan->max_power);
+       default:
+               break;
+       }
+       return chandef->chan->max_power;
+}
+
+/**
  * enum survey_info_flags - survey information flags
  *
  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -490,7 +517,7 @@ enum survey_info_flags {
  * @channel: the channel this survey record reports, mandatory
  * @filled: bitflag of flags from &enum survey_info_flags
  * @noise: channel noise in dBm. This and all following fields are
- *     optional
+ *     optional
  * @channel_time: amount of time in ms the radio spent on the channel
  * @channel_time_busy: amount of time the primary channel was sensed busy
  * @channel_time_ext_busy: amount of time the extension channel was sensed busy
@@ -546,9 +573,9 @@ struct cfg80211_crypto_settings {
 /**
  * struct cfg80211_beacon_data - beacon data
  * @head: head portion of beacon (before TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @tail: tail portion of beacon (after TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @head_len: length of @head
  * @tail_len: length of @tail
  * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
@@ -764,7 +791,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_PLINK_STATE: @plink_state filled
  * @STATION_INFO_SIGNAL: @signal filled
  * @STATION_INFO_TX_BITRATE: @txrate fields are filled
- *  (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
+ *     (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
  * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
  * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
  * @STATION_INFO_TX_RETRIES: @tx_retries filled
@@ -1285,6 +1312,7 @@ struct cfg80211_ssid {
  * @n_ssids: number of SSIDs
  * @channels: channels to scan on.
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
  * @flags: bit field of flags controlling operation
@@ -1300,6 +1328,7 @@ struct cfg80211_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        const u8 *ie;
        size_t ie_len;
        u32 flags;
@@ -1333,6 +1362,7 @@ struct cfg80211_match_set {
  * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
  * @n_ssids: number of SSIDs
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
@@ -1352,6 +1382,7 @@ struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        u32 interval;
        const u8 *ie;
        size_t ie_len;
@@ -1403,6 +1434,7 @@ struct cfg80211_bss_ies {
  * for use in scan results and similar.
  *
  * @channel: channel this BSS is on
+ * @scan_width: width of the control channel
  * @bssid: BSSID of the BSS
  * @beacon_interval: the beacon interval as from the frame
  * @capability: the capability field in host byte order
@@ -1424,6 +1456,7 @@ struct cfg80211_bss_ies {
  */
 struct cfg80211_bss {
        struct ieee80211_channel *channel;
+       enum nl80211_bss_scan_width scan_width;
 
        const struct cfg80211_bss_ies __rcu *ies;
        const struct cfg80211_bss_ies __rcu *beacon_ies;
@@ -1509,7 +1542,7 @@ enum cfg80211_assoc_req_flags {
  * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa: VHT capability override
  * @vht_capa_mask: VHT capability mask indicating which fields to use
@@ -1592,6 +1625,9 @@ struct cfg80211_disassoc_request {
  *     user space. Otherwise, port is marked authorized by default.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
+ *     will be used in ht_capa.  Un-supported values will be ignored.
+ * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
        u8 *ssid;
@@ -1605,6 +1641,8 @@ struct cfg80211_ibss_params {
        bool privacy;
        bool control_port;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       struct ieee80211_ht_cap ht_capa;
+       struct ieee80211_ht_cap ht_capa_mask;
 };
 
 /**
@@ -1630,9 +1668,9 @@ struct cfg80211_ibss_params {
  * @key: WEP key for shared key authentication
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @bg_scan_period:  Background scan period in seconds
- *   or -1 to indicate that default value is to be used.
+ *     or -1 to indicate that default value is to be used.
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa:  VHT Capability overrides
  * @vht_capa_mask: The bits of vht_capa which are to be used.
@@ -1698,7 +1736,7 @@ struct cfg80211_pmksa {
 };
 
 /**
- * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern
+ * struct cfg80211_pkt_pattern - packet pattern
  * @mask: bitmask where to match pattern and where to ignore bytes,
  *     one bit per byte, in same format as nl80211
  * @pattern: bytes to match where bitmask is 1
@@ -1708,7 +1746,7 @@ struct cfg80211_pmksa {
  * Internal note: @mask and @pattern are allocated in one chunk of
  * memory, free @mask only!
  */
-struct cfg80211_wowlan_trig_pkt_pattern {
+struct cfg80211_pkt_pattern {
        u8 *mask, *pattern;
        int pattern_len;
        int pkt_offset;
@@ -1770,12 +1808,41 @@ struct cfg80211_wowlan {
        bool any, disconnect, magic_pkt, gtk_rekey_failure,
             eap_identity_req, four_way_handshake,
             rfkill_release;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns;
+       struct cfg80211_pkt_pattern *patterns;
        struct cfg80211_wowlan_tcp *tcp;
        int n_patterns;
 };
 
 /**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ *     see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+       int delay;
+       enum nl80211_coalesce_condition condition;
+       struct cfg80211_pkt_pattern *patterns;
+       int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+       struct cfg80211_coalesce_rules *rules;
+       int n_rules;
+};
+
+/**
  * struct cfg80211_wowlan_wakeup - wakeup report
  * @disconnect: woke up by getting disconnected
  * @magic_pkt: woke up by receiving magic packet
@@ -2071,6 +2138,7 @@ struct cfg80211_update_ft_ies_params {
  *     driver can take the most appropriate actions.
  * @crit_proto_stop: Indicates critical protocol no longer needs increased link
  *     reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2306,6 +2374,8 @@ struct cfg80211_ops {
                                    u16 duration);
        void    (*crit_proto_stop)(struct wiphy *wiphy,
                                   struct wireless_dev *wdev);
+       int     (*set_coalesce)(struct wiphy *wiphy,
+                               struct cfg80211_coalesce *coalesce);
 };
 
 /*
@@ -2532,6 +2602,25 @@ struct wiphy_wowlan_support {
 };
 
 /**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ *     (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+       int n_rules;
+       int max_delay;
+       int n_patterns;
+       int pattern_max_len;
+       int pattern_min_len;
+       int max_pkt_offset;
+};
+
+/**
  * struct wiphy - wireless hardware description
  * @reg_notifier: the driver's regulatory notification callback,
  *     note that if your driver uses wiphy_apply_custom_regulatory()
@@ -2641,6 +2730,7 @@ struct wiphy_wowlan_support {
  *     802.11-2012 8.4.2.29 for the defined fields.
  * @extended_capabilities_mask: mask of the valid values
  * @extended_capabilities_len: length of the extended capabilities
+ * @coalesce: packet coalescing support information
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2750,6 +2840,8 @@ struct wiphy {
        const struct iw_handler_def *wext;
 #endif
 
+       const struct wiphy_coalesce_support *coalesce;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3063,11 +3155,13 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 /**
  * ieee80211_mandatory_rates - get mandatory rates for a given band
  * @sband: the band to look for rates in
+ * @scan_width: width of the control channel
  *
  * This function returns a bitmap of the mandatory rates for the given
  * band, bits are set according to the rate position in the bitrates array.
  */
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width);
 
 /*
  * Radiotap parsing functions -- for controlled injection support
@@ -3379,10 +3473,11 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy);
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
 
 /**
- * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
+ * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @mgmt: the management frame (probe response or beacon)
  * @len: length of the management frame
  * @signal: the signal strength, type depends on the wiphy's signal_type
@@ -3395,16 +3490,29 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_channel *channel,
                          struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp);
+                         s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width_frame(wiphy, channel,
+                                              NL80211_BSS_CHAN_WIDTH_20,
+                                              mgmt, len, signal, gfp);
+}
 
 /**
  * cfg80211_inform_bss - inform cfg80211 of a new BSS
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @bssid: the BSSID of the BSS
  * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
  * @capability: the capability field sent by the peer
@@ -3421,11 +3529,26 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss(struct wiphy *wiphy,
                    struct ieee80211_channel *channel,
                    const u8 *bssid, u64 tsf, u16 capability,
                    u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp);
+                   s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width(wiphy, channel,
+                                        NL80211_BSS_CHAN_WIDTH_20,
+                                        bssid, tsf, capability,
+                                        beacon_interval, ie, ielen, signal,
+                                        gfp);
+}
 
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      struct ieee80211_channel *channel,
@@ -3471,6 +3594,19 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
  */
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return NL80211_BSS_CHAN_WIDTH_5;
+       case NL80211_CHAN_WIDTH_10:
+               return NL80211_BSS_CHAN_WIDTH_10;
+       default:
+               return NL80211_BSS_CHAN_WIDTH_20;
+       }
+}
+
 /**
  * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
index c6d07cb..8b5b714 100644 (file)
@@ -230,6 +230,10 @@ enum ieee80211_radiotap_type {
 #define        IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
 #define        IEEE80211_CHAN_DYN      0x0400  /* Dynamic CCK-OFDM channel */
 #define        IEEE80211_CHAN_GFSK     0x0800  /* GFSK channel (FHSS PHY) */
+#define        IEEE80211_CHAN_GSM      0x1000  /* GSM (900 MHz) */
+#define        IEEE80211_CHAN_STURBO   0x2000  /* Static Turbo */
+#define        IEEE80211_CHAN_HALF     0x4000  /* Half channel (10 MHz wide) */
+#define        IEEE80211_CHAN_QUARTER  0x8000  /* Quarter channel (5 MHz wide) */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
 #define        IEEE80211_RADIOTAP_F_CFP        0x01    /* sent/received
index 5b7a3da..3124036 100644 (file)
@@ -811,6 +811,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -839,6 +841,8 @@ enum mac80211_rx_flags {
        RX_FLAG_80P80MHZ                = BIT(24),
        RX_FLAG_160MHZ                  = BIT(25),
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
+       RX_FLAG_10MHZ                   = BIT(28),
+       RX_FLAG_5MHZ                    = BIT(29),
 };
 
 #define RX_FLAG_STBC_SHIFT             26
@@ -1004,11 +1008,11 @@ enum ieee80211_smps_mode {
  * @radar_enabled: whether radar detection is enabled
  *
  * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
- *    (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
- *    but actually means the number of transmissions not the number of retries
+ *     (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ *     but actually means the number of transmissions not the number of retries
  * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
- *    frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
- *    number of transmissions not the number of retries
+ *     frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ *     number of transmissions not the number of retries
  *
  * @smps_mode: spatial multiplexing powersave mode; note that
  *     %IEEE80211_SMPS_STATIC is used when the device is not
@@ -1092,7 +1096,7 @@ enum ieee80211_vif_flags {
  *     be off when it is %NULL there can still be races and packets could be
  *     processed after it switches back to %NULL.
  * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
- *      interface debug files. Note that it will be NULL for the virtual
+ *     interface debug files. Note that it will be NULL for the virtual
  *     monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
@@ -1425,10 +1429,10 @@ struct ieee80211_tx_control {
  *     the stack.
  *
  * @IEEE80211_HW_CONNECTION_MONITOR:
- *      The hardware performs its own connection monitoring, including
- *      periodic keep-alives to the AP and probing the AP on beacon loss.
- *      When this flag is set, signaling beacon-loss will cause an immediate
- *      change to disassociated state.
+ *     The hardware performs its own connection monitoring, including
+ *     periodic keep-alives to the AP and probing the AP on beacon loss.
+ *     When this flag is set, signaling beacon-loss will cause an immediate
+ *     change to disassociated state.
  *
  * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
  *     This device needs to get data from beacon before association (i.e.
@@ -1526,10 +1530,10 @@ enum ieee80211_hw_flags {
  * @channel_change_time: time (in microseconds) it takes to change channels.
  *
  * @max_signal: Maximum value for signal (rssi) in RX information, used
- *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
+ *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
  *
  * @max_listen_interval: max listen interval in units of beacon interval
- *     that HW supports
+ *     that HW supports
  *
  * @queues: number of available hardware transmit queues for
  *     data packets. WMM/QoS requires at least four, these
@@ -2443,7 +2447,7 @@ enum ieee80211_roc_type {
  *     The callback can sleep.
  *
  * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
- *      Currently, this is only used for IBSS mode debugging. Is not a
+ *     Currently, this is only used for IBSS mode debugging. Is not a
  *     required function.
  *     The callback can sleep.
  *
@@ -4204,8 +4208,10 @@ struct rate_control_ops {
 
        void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
        void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta);
        void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed);
        void (*free_sta)(void *priv, struct ieee80211_sta *sta,
index 861e5eb..eb68735 100644 (file)
  */
 
 /**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *     return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +838,9 @@ enum nl80211_commands {
        NL80211_CMD_CRIT_PROTOCOL_START,
        NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+       NL80211_CMD_GET_COALESCE,
+       NL80211_CMD_SET_COALESCE,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1436,6 +1467,8 @@ enum nl80211_commands {
  *     allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *     update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1769,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PEER_AID,
 
+       NL80211_ATTR_COALESCE_RULE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2773,6 +2808,21 @@ enum nl80211_chan_width {
 };
 
 /**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+       NL80211_BSS_CHAN_WIDTH_20,
+       NL80211_BSS_CHAN_WIDTH_10,
+       NL80211_BSS_CHAN_WIDTH_5,
+};
+
+/**
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
@@ -2796,6 +2846,8 @@ enum nl80211_chan_width {
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *     elements from a Beacon frame (bin); not present if no Beacon frame has
  *     yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *     (u32, enum nl80211_bss_scan_width)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2812,6 +2864,7 @@ enum nl80211_bss {
        NL80211_BSS_STATUS,
        NL80211_BSS_SEEN_MS_AGO,
        NL80211_BSS_BEACON_IES,
+       NL80211_BSS_CHAN_WIDTH,
 
        /* keep last */
        __NL80211_BSS_AFTER_LAST,
@@ -3060,11 +3113,11 @@ enum nl80211_tx_power_setting {
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *     a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *     a bit for each byte in the pattern. The lowest-order bit corresponds
  *     to the first byte of the pattern, but the bytes of the pattern are
  *     in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3075,39 +3128,50 @@ enum nl80211_tx_power_setting {
  *     Note that the pattern matching is done as though frames were not
  *     802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *     first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *     these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-       __NL80211_WOWLAN_PKTPAT_INVALID,
-       NL80211_WOWLAN_PKTPAT_MASK,
-       NL80211_WOWLAN_PKTPAT_PATTERN,
-       NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+       __NL80211_PKTPAT_INVALID,
+       NL80211_PKTPAT_MASK,
+       NL80211_PKTPAT_PATTERN,
+       NL80211_PKTPAT_OFFSET,
 
-       NUM_NL80211_WOWLAN_PKTPAT,
-       MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+       NUM_NL80211_PKTPAT,
+       MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
        __u32 max_patterns;
        __u32 min_pattern_len;
        __u32 max_pattern_len;
        __u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3127,7 +3191,7 @@ struct nl80211_wowlan_pattern_support {
  *     pattern matching is done after the packet is converted to the MSDU.
  *
  *     In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *     carrying a &struct nl80211_wowlan_pattern_support.
+ *     carrying a &struct nl80211_pattern_support.
  *
  *     When reporting wakeup. it is a u32 attribute containing the 0-based
  *     index of the pattern that caused the wakeup, in the patterns passed
@@ -3284,7 +3348,7 @@ struct nl80211_wowlan_tcp_data_token_feature {
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *     u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *     feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *     feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *     but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3309,6 +3373,55 @@ enum nl80211_wowlan_tcp_attrs {
 };
 
 /**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+       __u32 max_rules;
+       struct nl80211_pattern_support pat;
+       __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *     see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *     after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+       __NL80211_COALESCE_RULE_INVALID,
+       NL80211_ATTR_COALESCE_RULE_DELAY,
+       NL80211_ATTR_COALESCE_RULE_CONDITION,
+       NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+       /* keep last */
+       NUM_NL80211_ATTR_COALESCE_RULE,
+       NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *     in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *     in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+       NL80211_COALESCE_CONDITION_MATCH,
+       NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
  * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
index 43dd752..973594b 100644 (file)
@@ -395,9 +395,13 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                rinfo->nss = ieee80211_rate_get_vht_nss(rate);
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
+
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+               brate = sband->bitrates[rate->idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
        if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@@ -422,11 +426,13 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->mcs = sta->last_rx_rate_idx;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
 
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy =
-                       sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
 
        if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@@ -1192,8 +1198,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                struct station_parameters *params)
 {
        int ret = 0;
-       u32 rates;
-       int i, j;
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
@@ -1286,16 +1290,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->listen_interval = params->listen_interval;
 
        if (params->supported_rates) {
-               rates = 0;
-
-               for (i = 0; i < params->supported_rates_len; i++) {
-                       int rate = (params->supported_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sta->sta.supp_rates[band] = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        sband, params->supported_rates,
+                                        params->supported_rates_len,
+                                        &sta->sta.supp_rates[band]);
        }
 
        if (params->ht_capa)
@@ -1958,18 +1956,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
        }
 
        if (params->basic_rates) {
-               int i, j;
-               u32 rates = 0;
-               struct ieee80211_supported_band *sband = wiphy->bands[band];
-
-               for (i = 0; i < params->basic_rates_len; i++) {
-                       int rate = (params->basic_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sdata->vif.bss_conf.basic_rates = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        wiphy->bands[band],
+                                        params->basic_rates,
+                                        params->basic_rates_len,
+                                        &sdata->vif.bss_conf.basic_rates);
                changed |= BSS_CHANGED_BASIC_RATES;
        }
 
index f83534f..529bf58 100644 (file)
 #include "ieee80211_i.h"
 #include "rate.h"
 
-static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
+                                 struct ieee80211_ht_cap *ht_capa_mask,
                                  struct ieee80211_sta_ht_cap *ht_cap,
                                  u16 flag)
 {
        __le16 le_flag = cpu_to_le16(flag);
-       if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
-               if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+       if (ht_capa_mask->cap_info & le_flag) {
+               if (!(ht_capa->cap_info & le_flag))
                        ht_cap->cap &= ~flag;
        }
 }
@@ -33,13 +34,30 @@ static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_sta_ht_cap *ht_cap)
 {
-       u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
-       u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+       struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
+       u8 *scaps, *smask;
        int i;
 
        if (!ht_cap->ht_supported)
                return;
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+               ht_capa = &sdata->u.mgd.ht_capa;
+               ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ht_capa = &sdata->u.ibss.ht_capa;
+               ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       scaps = (u8 *)(&ht_capa->mcs.rx_mask);
+       smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
+
        /* NOTE:  If you add more over-rides here, update register_hw
         * ht_capa_mod_msk logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
@@ -55,28 +73,32 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        }
 
        /* Force removal of HT-40 capabilities? */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_40);
 
        /* Allow user to disable SGI-20 (SGI-40 is handled above) */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_20);
 
        /* Allow user to disable the max-AMSDU bit. */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_MAX_AMSDU);
 
        /* Allow user to decrease AMPDU factor */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
-               u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
-                       & IEEE80211_HT_AMPDU_PARM_FACTOR;
+               u8 n = ht_capa->ampdu_params_info &
+                      IEEE80211_HT_AMPDU_PARM_FACTOR;
                if (n < ht_cap->ampdu_factor)
                        ht_cap->ampdu_factor = n;
        }
 
        /* Allow the user to increase AMPDU density. */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_DENSITY) {
-               u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+               u8 n = (ht_capa->ampdu_params_info &
                        IEEE80211_HT_AMPDU_PARM_DENSITY)
                        >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
                if (n > ht_cap->ampdu_density)
@@ -112,7 +134,8 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
+            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
index ea7b9c2..5e6836c 100644 (file)
@@ -43,16 +43,18 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
-       int ratesi;
+       int rates_n = 0, i, ri;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
        struct ieee80211_supported_band *sband;
        struct cfg80211_bss *bss;
-       u32 bss_change;
-       u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+       u32 bss_change, rate_flags, rates = 0, rates_added = 0;
        struct cfg80211_chan_def chandef;
+       enum nl80211_bss_scan_width scan_width;
+       bool have_higher_than_11mbit = false;
        struct beacon_data *presp;
        int frame_len;
+       int shift;
 
        sdata_assert_lock(sdata);
 
@@ -83,6 +85,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        chandef = ifibss->chandef;
        if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+               if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+                   chandef.width == NL80211_CHAN_WIDTH_10 ||
+                   chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+                   chandef.width == NL80211_CHAN_WIDTH_20) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
                chandef.width = NL80211_CHAN_WIDTH_20;
                chandef.center_freq1 = chan->center_freq;
        }
@@ -99,6 +109,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
 
        sband = local->hw.wiphy->bands[chan->band];
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        /* Build IBSS probe response */
        frame_len = sizeof(struct ieee80211_hdr_3addr) +
@@ -134,15 +145,33 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        memcpy(pos, ifibss->ssid, ifibss->ssid_len);
        pos += ifibss->ssid_len;
 
-       rates = min_t(int, 8, sband->n_bitrates);
+       rate_flags = ieee80211_chandef_rate_flags(&chandef);
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               if (sband->bitrates[i].bitrate > 110)
+                       have_higher_than_11mbit = true;
+
+               rates |= BIT(i);
+               rates_n++;
+       }
+
        *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = rates;
-       for (i = 0; i < rates; i++) {
-               int rate = sband->bitrates[i].bitrate;
+       *pos++ = min_t(int, 8, rates_n);
+       for (ri = 0; ri < sband->n_bitrates; ri++) {
+               int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                       5 * (1 << shift));
                u8 basic = 0;
-               if (basic_rates & BIT(i))
+               if (!(rates & BIT(ri)))
+                       continue;
+
+               if (basic_rates & BIT(ri))
                        basic = 0x80;
-               *pos++ = basic | (u8) (rate / 5);
+               *pos++ = basic | (u8) rate;
+               if (++rates_added == 8) {
+                       ri++; /* continue at next rate for EXT_SUPP_RATES */
+                       break;
+               }
        }
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
@@ -157,15 +186,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        *pos++ = 0;
        *pos++ = 0;
 
-       if (sband->n_bitrates > 8) {
+       /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
+       if (rates_n > 8) {
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = sband->n_bitrates - 8;
-               for (i = 8; i < sband->n_bitrates; i++) {
-                       int rate = sband->bitrates[i].bitrate;
+               *pos++ = rates_n - 8;
+               for (; ri < sband->n_bitrates; ri++) {
+                       int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                               5 * (1 << shift));
                        u8 basic = 0;
-                       if (basic_rates & BIT(i))
+                       if (!(rates & BIT(ri)))
+                               continue;
+
+                       if (basic_rates & BIT(ri))
                                basic = 0x80;
-                       *pos++ = basic | (u8) (rate / 5);
+                       *pos++ = basic | (u8) rate;
                }
        }
 
@@ -179,8 +213,12 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
            chandef.width != NL80211_CHAN_WIDTH_5 &&
            chandef.width != NL80211_CHAN_WIDTH_10 &&
            sband->ht_cap.ht_supported) {
-               pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
-                                               sband->ht_cap.cap);
+               struct ieee80211_sta_ht_cap ht_cap;
+
+               memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+               ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+               pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
                /*
                 * Note: According to 802.11n-2009 9.13.3.1, HT Protection
                 * field and RIFS Mode are reserved in IBSS mode, therefore
@@ -236,18 +274,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
        bss_change |= BSS_CHANGED_ERP_SLOT;
 
+       /* cf. IEEE 802.11 9.2.12 */
+       if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+       else
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
        sdata->vif.bss_conf.ibss_joined = true;
        sdata->vif.bss_conf.ibss_creator = creator;
        ieee80211_bss_info_change_notify(sdata, bss_change);
 
-       ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
+       ieee80211_set_wmm_default(sdata, true);
 
        ifibss->state = IEEE80211_IBSS_MLME_JOINED;
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-       bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
-                                       mgmt, presp->head_len, 0, GFP_KERNEL);
+       scan_width = cfg80211_chandef_to_scan_width(&chandef);
+       bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
+                                             scan_width, mgmt,
+                                             presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
@@ -264,6 +310,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        u16 beacon_int = cbss->beacon_interval;
        const struct cfg80211_bss_ies *ies;
        u64 tsf;
+       u32 rate_flags;
+       int shift;
 
        sdata_assert_lock(sdata);
 
@@ -271,15 +319,24 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                beacon_int = 10;
 
        sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        basic_rates = 0;
 
        for (i = 0; i < bss->supp_rates_len; i++) {
-               int rate = (bss->supp_rates[i] & 0x7f) * 5;
+               int rate = bss->supp_rates[i] & 0x7f;
                bool is_basic = !!(bss->supp_rates[i] & 0x80);
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            5 * (1 << shift));
+                       if (brate == rate) {
                                if (is_basic)
                                        basic_rates |= BIT(j);
                                break;
@@ -335,6 +392,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -363,6 +421,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        if (WARN_ON_ONCE(!chanctx_conf))
                return NULL;
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
@@ -376,7 +435,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        return ieee80211_ibss_finish_sta(sta);
 }
@@ -440,6 +499,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        u64 beacon_timestamp, rx_timestamp;
        u32 supp_rates = 0;
        enum ieee80211_band band = rx_status->band;
+       enum nl80211_bss_scan_width scan_width;
        struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
        bool rates_updated = false;
 
@@ -461,16 +521,22 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                sta = sta_info_get(sdata, mgmt->sa);
 
                if (elems->supp_rates) {
-                       supp_rates = ieee80211_sta_get_rates(local, elems,
+                       supp_rates = ieee80211_sta_get_rates(sdata, elems,
                                                             band, NULL);
                        if (sta) {
                                u32 prev_rates;
 
                                prev_rates = sta->sta.supp_rates[band];
                                /* make sure mandatory rates are always added */
-                               sta->sta.supp_rates[band] = supp_rates |
-                                       ieee80211_mandatory_rates(sband);
+                               scan_width = NL80211_BSS_CHAN_WIDTH_20;
+                               if (rx_status->flag & RX_FLAG_5MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_5;
+                               if (rx_status->flag & RX_FLAG_10MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_10;
 
+                               sta->sta.supp_rates[band] = supp_rates |
+                                       ieee80211_mandatory_rates(sband,
+                                                                 scan_width);
                                if (sta->sta.supp_rates[band] != prev_rates) {
                                        ibss_dbg(sdata,
                                                 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
@@ -585,7 +651,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                         "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
                         mgmt->bssid);
                ieee80211_sta_join_ibss(sdata, bss);
-               supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
+               supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
                ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
                                       supp_rates);
                rcu_read_unlock();
@@ -604,6 +670,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -629,6 +696,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
                return;
        }
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -640,7 +708,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        spin_lock(&ifibss->incomplete_lock);
        list_add(&sta->list, &ifibss->incomplete_stations);
@@ -679,6 +747,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       enum nl80211_bss_scan_width scan_width;
 
        sdata_assert_lock(sdata);
 
@@ -700,8 +769,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
+       scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
        ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
-                                   NULL);
+                                   NULL, scan_width);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -751,6 +821,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        struct cfg80211_bss *cbss;
        struct ieee80211_channel *chan = NULL;
        const u8 *bssid = NULL;
+       enum nl80211_bss_scan_width scan_width;
        int active_ibss;
        u16 capability;
 
@@ -799,8 +870,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_SCAN_INTERVAL)) {
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
+               scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
                ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-                                           ifibss->ssid_len, chan);
+                                           ifibss->ssid_len, chan,
+                                           scan_width);
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -1020,6 +1093,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params)
 {
        u32 changed = 0;
+       u32 rate_flags;
+       struct ieee80211_supported_band *sband;
+       int i;
 
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1030,6 +1106,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
        sdata->u.ibss.basic_rates = params->basic_rates;
+
+       /* fix basic_rates if channel does not support these rates */
+       rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
+       sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       sdata->u.ibss.basic_rates &= ~BIT(i);
+       }
        memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
               sizeof(params->mcast_rate));
 
@@ -1051,6 +1135,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
        sdata->u.ibss.ssid_len = params->ssid_len;
 
+       memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
+              sizeof(sdata->u.ibss.ht_capa));
+       memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
+              sizeof(sdata->u.ibss.ht_capa_mask));
+
        /*
         * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
         * reserved, but an HT STA shall protect HT transmissions as though
@@ -1131,6 +1220,11 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        presp = rcu_dereference_protected(ifibss->presp,
                                          lockdep_is_held(&sdata->wdev.mtx));
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+
+       /* on the next join, re-program HT parameters */
+       memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
+       memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
+
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
        sdata->vif.bss_conf.enable_beacon = false;
index 8412a30..3d32df1 100644 (file)
@@ -509,6 +509,9 @@ struct ieee80211_if_ibss {
        /* probe response/beacon for IBSS */
        struct beacon_data __rcu *presp;
 
+       struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+       struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+
        spinlock_t incomplete_lock;
        struct list_head incomplete_stations;
 
@@ -809,6 +812,34 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
        return band;
 }
 
+static inline int
+ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return 2;
+       case NL80211_CHAN_WIDTH_10:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static inline int
+ieee80211_vif_get_shift(struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       int shift = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (chanctx_conf)
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+       rcu_read_unlock();
+
+       return shift;
+}
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
@@ -1026,7 +1057,7 @@ struct ieee80211_local {
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
        struct cfg80211_scan_request *scan_req, *hw_scan_req;
-       struct ieee80211_channel *scan_channel;
+       struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
        int scan_ies_len;
@@ -1306,7 +1337,8 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan);
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1465,7 +1497,8 @@ extern void *mac80211_wiphy_privid; /* for wiphy privid */
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
                        enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble);
+                            int rate, int erp, int short_preamble,
+                            int shift);
 void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
                                     struct ieee80211_hdr *hdr, const u8 *tsc,
                                     gfp_t gfp);
@@ -1569,7 +1602,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel);
+                            struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
@@ -1582,10 +1615,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              u32 ratemask, bool directed, u32 tx_flags,
                              struct ieee80211_channel *channel, bool scan);
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
 int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
@@ -1602,6 +1632,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               u16 prot_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                               u32 cap);
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates);
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band);
index cc11759..4c41c11 100644 (file)
@@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
                return false;
        }
 
-       power = chanctx_conf->def.chan->max_power;
+       power = ieee80211_chandef_max_power(&chanctx_conf->def);
        rcu_read_unlock();
 
        if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
index 091088a..25eb35b 100644 (file)
@@ -102,17 +102,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
 
        offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
 
-       if (local->scan_channel) {
-               chandef.chan = local->scan_channel;
-               /* If scanning on oper channel, use whatever channel-type
-                * is currently in use.
-                */
-               if (chandef.chan == local->_oper_chandef.chan) {
-                       chandef = local->_oper_chandef;
-               } else {
-                       chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
-                       chandef.center_freq1 = chandef.chan->center_freq;
-               }
+       if (local->scan_chandef.chan) {
+               chandef = local->scan_chandef;
        } else if (local->tmp_channel) {
                chandef.chan = local->tmp_channel;
                chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -151,7 +142,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
                changed |= IEEE80211_CONF_CHANGE_SMPS;
        }
 
-       power = chandef.chan->max_power;
+       power = ieee80211_chandef_max_power(&chandef);
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
index 447f41b..885a5f6 100644 (file)
@@ -62,7 +62,6 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
                        struct ieee802_11_elems *ie)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee80211_local *local = sdata->local;
        u32 basic_rates = 0;
        struct cfg80211_chan_def sta_chan_def;
 
@@ -85,7 +84,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
             (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
                return false;
 
-       ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
+       ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
                                &basic_rates);
 
        if (sdata->vif.bss_conf.basic_rates != basic_rates)
@@ -274,7 +273,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
-       *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
+       *pos = 0x00;
+       *pos |= ifmsh->mshcfg.dot11MeshForwarding ?
+                       IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
        *pos |= ifmsh->accepting_plinks ?
                        IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
        /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
index 02c05fa..6b65d50 100644 (file)
@@ -379,7 +379,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        u32 rates, basic_rates = 0, changed = 0;
 
        sband = local->hw.wiphy->bands[band];
-       rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
+       rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
 
        spin_lock_bh(&sta->lock);
        sta->last_rx = jiffies;
index cc9e02d..21bccd8 100644 (file)
@@ -489,27 +489,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
 
 /* frame sending functions */
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-                                     struct ieee80211_supported_band *sband,
-                                     u32 *rates)
-{
-       int i, j, count;
-       *rates = 0;
-       count = 0;
-       for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7F) * 5;
-
-               for (j = 0; j < sband->n_bitrates; j++)
-                       if (sband->bitrates[j].bitrate == rate) {
-                               *rates |= BIT(j);
-                               count++;
-                               break;
-                       }
-       }
-
-       return count;
-}
-
 static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, u8 ap_ht_param,
                                struct ieee80211_supported_band *sband,
@@ -628,12 +607,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgmt *mgmt;
        u8 *pos, qos_info;
        size_t offset = 0, noffset;
-       int i, count, rates_len, supp_rates_len;
+       int i, count, rates_len, supp_rates_len, shift;
        u16 capab;
        struct ieee80211_supported_band *sband;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *chan;
-       u32 rates = 0;
+       u32 rate_flags, rates = 0;
 
        sdata_assert_lock(sdata);
 
@@ -644,8 +623,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                return;
        }
        chan = chanctx_conf->def.chan;
+       rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
        rcu_read_unlock();
        sband = local->hw.wiphy->bands[chan->band];
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (assoc_data->supp_rates_len) {
                /*
@@ -654,17 +635,24 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                 * in the association request (e.g. D-Link DAP 1353 in
                 * b-only mode)...
                 */
-               rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
-                                                      assoc_data->supp_rates_len,
-                                                      sband, &rates);
+               rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
+                                                    assoc_data->supp_rates,
+                                                    assoc_data->supp_rates_len,
+                                                    &rates);
        } else {
                /*
                 * In case AP not provide any supported rates information
                 * before association, we send information element(s) with
                 * all rates that we support.
                 */
-               rates = ~0;
-               rates_len = sband->n_bitrates;
+               rates_len = 0;
+               for (i = 0; i < sband->n_bitrates; i++) {
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       rates |= BIT(i);
+                       rates_len++;
+               }
        }
 
        skb = alloc_skb(local->hw.extra_tx_headroom +
@@ -741,8 +729,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        count = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if (BIT(i) & rates) {
-                       int rate = sband->bitrates[i].bitrate;
-                       *pos++ = (u8) (rate / 5);
+                       int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                               5 * (1 << shift));
+                       *pos++ = (u8) rate;
                        if (++count == 8)
                                break;
                }
@@ -755,8 +744,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 
                for (i++; i < sband->n_bitrates; i++) {
                        if (BIT(i) & rates) {
-                               int rate = sband->bitrates[i].bitrate;
-                               *pos++ = (u8) (rate / 5);
+                               int rate;
+                               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                                   5 * (1 << shift));
+                               *pos++ = (u8) rate;
                        }
                }
        }
@@ -767,7 +758,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                *pos++ = WLAN_EID_PWR_CAPABILITY;
                *pos++ = 2;
                *pos++ = 0; /* min tx power */
-               *pos++ = chan->max_power; /* max tx power */
+                /* max tx power */
+               *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
 
                /* 2. supported channels */
                /* TODO: get this in reg domain format */
@@ -2443,15 +2435,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                                u8 *supp_rates, unsigned int supp_rates_len,
                                u32 *rates, u32 *basic_rates,
                                bool *have_higher_than_11mbit,
-                               int *min_rate, int *min_rate_index)
+                               int *min_rate, int *min_rate_index,
+                               int shift, u32 rate_flags)
 {
        int i, j;
 
        for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7f) * 5;
+               int rate = supp_rates[i] & 0x7f;
                bool is_basic = !!(supp_rates[i] & 0x80);
 
-               if (rate > 110)
+               if ((rate * 5 * (1 << shift)) > 110)
                        *have_higher_than_11mbit = true;
 
                /*
@@ -2467,12 +2460,20 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                        continue;
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       struct ieee80211_rate *br;
+                       int brate;
+
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
                                *rates |= BIT(j);
                                if (is_basic)
                                        *basic_rates |= BIT(j);
-                               if (rate < *min_rate) {
-                                       *min_rate = rate;
+                               if ((rate * 5) < *min_rate) {
+                                       *min_rate = rate * 5;
                                        *min_rate_index = j;
                                }
                                break;
@@ -3902,27 +3903,40 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                if (!new_sta)
                        return -ENOMEM;
        }
-
        if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
+               struct ieee80211_chanctx_conf *chanctx_conf;
                struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
+               int shift;
+               u32 rate_flags;
 
                sband = local->hw.wiphy->bands[cbss->channel->band];
 
                err = ieee80211_prep_channel(sdata, cbss);
                if (err) {
                        sta_info_free(local, new_sta);
-                       return err;
+                       return -EINVAL;
                }
+               shift = ieee80211_vif_get_shift(&sdata->vif);
+
+               rcu_read_lock();
+               chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+               rcu_read_unlock();
 
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                                    &rates, &basic_rates,
                                    &have_higher_than_11mbit,
-                                   &min_rate, &min_rate_index);
+                                   &min_rate, &min_rate_index,
+                                   shift, rate_flags);
 
                /*
                 * This used to be a workaround for basic rates missing
index 30d58d2..ba63ac8 100644 (file)
@@ -232,37 +232,28 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
        /* could not find a basic rate; use original selection */
 }
 
-static inline s8
-rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta)
+static void __rate_control_send_low(struct ieee80211_hw *hw,
+                                   struct ieee80211_supported_band *sband,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_tx_info *info)
 {
        int i;
+       u32 rate_flags =
+               ieee80211_chandef_rate_flags(&hw->conf.chandef);
 
+       if ((sband->band == IEEE80211_BAND_2GHZ) &&
+           (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+               rate_flags |= IEEE80211_RATE_ERP_G;
+
+       info->control.rates[0].idx = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *srate = &sband->bitrates[i];
-               if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
-                   (srate->bitrate == 55) || (srate->bitrate == 110))
+               if (!rate_supported(sta, sband->band, i))
                        continue;
 
-               if (rate_supported(sta, sband->band, i))
-                       return i;
+               info->control.rates[0].idx = i;
+               break;
        }
-
-       /* No matching rate found */
-       return 0;
-}
-
-static void __rate_control_send_low(struct ieee80211_hw *hw,
-                                   struct ieee80211_supported_band *sband,
-                                   struct ieee80211_sta *sta,
-                                   struct ieee80211_tx_info *info)
-{
-       if ((sband->band != IEEE80211_BAND_2GHZ) ||
-           !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
-               info->control.rates[0].idx = rate_lowest_index(sband, sta);
-       else
-               info->control.rates[0].idx =
-                       rate_lowest_non_cck_index(sband, sta);
+       WARN_ON_ONCE(i == sband->n_bitrates);
 
        info->control.rates[0].count =
                (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@@ -585,6 +576,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
        u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
        bool has_mcs_mask;
        u32 mask;
+       u32 rate_flags;
        int i;
 
        /*
@@ -594,6 +586,12 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
         */
        mask = sdata->rc_rateidx_mask[info->band];
        has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
+       rate_flags =
+               ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       for (i = 0; i < sband->n_bitrates; i++)
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       mask &= ~BIT(i);
+
        if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
                return;
 
index d35a5dd..5dedc56 100644 (file)
@@ -66,11 +66,12 @@ static inline void rate_control_rate_init(struct sta_info *sta)
        }
 
        sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
-       rcu_read_unlock();
 
        ieee80211_sta_set_rx_nss(sta);
 
-       ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
+       ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+                           priv_sta);
+       rcu_read_unlock();
        set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
 }
 
@@ -81,10 +82,21 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
        struct rate_control_ref *ref = local->rate_ctrl;
        struct ieee80211_sta *ista = &sta->sta;
        void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       if (ref && ref->ops->rate_update) {
+               rcu_read_lock();
 
-       if (ref && ref->ops->rate_update)
-               ref->ops->rate_update(ref->priv, sband, ista,
-                                     priv_sta, changed);
+               chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+                                     ista, priv_sta, changed);
+               rcu_read_unlock();
+       }
        drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
 }
 
index e6512e2..8b5f7ef 100644 (file)
@@ -383,14 +383,18 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
 static void
 calc_rate_durations(enum ieee80211_band band,
                    struct minstrel_rate *d,
-                   struct ieee80211_rate *rate)
+                   struct ieee80211_rate *rate,
+                   struct cfg80211_chan_def *chandef)
 {
        int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+       int shift = ieee80211_chandef_get_shift(chandef);
 
        d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
        d->ack_time = ieee80211_frame_duration(band, 10,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
 }
 
 static void
@@ -418,21 +422,25 @@ init_sample_table(struct minstrel_sta_info *mi)
 
 static void
 minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
-               struct ieee80211_sta *sta, void *priv_sta)
+                  struct cfg80211_chan_def *chandef,
+                  struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_sta_info *mi = priv_sta;
        struct minstrel_priv *mp = priv;
        struct ieee80211_rate *ctl_rate;
        unsigned int i, n = 0;
        unsigned int t_slot = 9; /* FIXME: get real slot time */
+       u32 rate_flags;
 
        mi->sta = sta;
        mi->lowest_rix = rate_lowest_index(sband, sta);
        ctl_rate = &sband->bitrates[mi->lowest_rix];
        mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
                                ctl_rate->bitrate,
-                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+                               ieee80211_chandef_get_shift(chandef));
 
+       rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
        mi->max_prob_rate = 0;
 
@@ -441,15 +449,22 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
                unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
                unsigned int tx_time_single;
                unsigned int cw = mp->cw_min;
+               int shift;
 
                if (!rate_supported(sta, sband->band, i))
                        continue;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                n++;
                memset(mr, 0, sizeof(*mr));
 
                mr->rix = i;
-               mr->bitrate = sband->bitrates[i].bitrate / 5;
-               calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+               shift = ieee80211_chandef_get_shift(chandef);
+               mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                          (1 << shift) * 5);
+               calc_rate_durations(sband->band, mr, &sband->bitrates[i],
+                                   chandef);
 
                /* calculate maximum number of retransmissions before
                 * fallback (based on maximum segment size) */
@@ -547,6 +562,7 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
 {
        static const int bitrates[4] = { 10, 20, 55, 110 };
        struct ieee80211_supported_band *sband;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        int i, j;
 
        sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -559,6 +575,9 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
                if (rate->flags & IEEE80211_RATE_ERP_G)
                        continue;
 
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
                        if (rate->bitrate != bitrates[j])
                                continue;
index f5aed96..6156942 100644 (file)
@@ -844,6 +844,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_priv *mp = priv;
@@ -869,8 +870,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        mi->sta = sta;
        mi->stats_update = jiffies;
 
-       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
-       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
+       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
+       mi->overhead += ack_dur;
        mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
 
        mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
@@ -939,22 +941,25 @@ use_legacy:
        memset(&msp->legacy, 0, sizeof(msp->legacy));
        msp->legacy.r = msp->ratelist;
        msp->legacy.sample_table = msp->sample_table;
-       return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
+       return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
+                                          &msp->legacy);
 }
 
 static void
 minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                     struct cfg80211_chan_def *chandef,
                       struct ieee80211_sta *sta, void *priv_sta)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void
 minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta,
                         u32 changed)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void *
index 502d3ec..958fad0 100644 (file)
@@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 
 static void
 rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                          struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct rc_pid_sta_info *spinfo = priv_sta;
index 2c5a79b..6b85f95 100644 (file)
@@ -87,11 +87,13 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        int len;
 
        /* always present fields */
-       len = sizeof(struct ieee80211_radiotap_header) + 9;
+       len = sizeof(struct ieee80211_radiotap_header) + 8;
 
-       /* allocate extra bitmap */
+       /* allocate extra bitmaps */
        if (status->vendor_radiotap_len)
                len += 4;
+       if (status->chains)
+               len += 4 * hweight8(status->chains);
 
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
@@ -100,6 +102,10 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                len += 1;
 
+       /* antenna field, if we don't have per-chain info */
+       if (!status->chains)
+               len += 1;
+
        /* padding for RX_FLAGS if necessary */
        len = ALIGN(len, 2);
 
@@ -116,6 +122,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
                len += 12;
        }
 
+       if (status->chains) {
+               /* antenna and antenna signal fields */
+               len += 2 * hweight8(status->chains);
+       }
+
        if (status->vendor_radiotap_len) {
                if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
                        status->vendor_radiotap_align = 1;
@@ -145,8 +156,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
+       __le32 *it_present;
+       u32 it_present_val;
        u16 rx_flags = 0;
-       int mpdulen;
+       u16 channel_flags = 0;
+       int mpdulen, chain;
+       unsigned long chains = status->chains;
 
        mpdulen = skb->len;
        if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@@ -154,25 +169,39 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
        memset(rthdr, 0, rtap_len);
+       it_present = &rthdr->it_present;
 
        /* radiotap header, set always present flags */
-       rthdr->it_present =
-               cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-                           (1 << IEEE80211_RADIOTAP_CHANNEL) |
-                           (1 << IEEE80211_RADIOTAP_ANTENNA) |
-                           (1 << IEEE80211_RADIOTAP_RX_FLAGS));
        rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+       it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+                        BIT(IEEE80211_RADIOTAP_CHANNEL) |
+                        BIT(IEEE80211_RADIOTAP_RX_FLAGS);
 
-       pos = (unsigned char *)(rthdr + 1);
+       if (!status->chains)
+               it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
+
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               it_present_val |=
+                       BIT(IEEE80211_RADIOTAP_EXT) |
+                       BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                                BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+       }
 
        if (status->vendor_radiotap_len) {
-               rthdr->it_present |=
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
-               put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
-               pos += 4;
+               it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                                 BIT(IEEE80211_RADIOTAP_EXT);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = status->vendor_radiotap_bitmap;
        }
 
+       put_unaligned_le32(it_present_val, it_present);
+
+       pos = (void *)(it_present + 1);
+
        /* the order of the following fields is important */
 
        /* IEEE80211_RADIOTAP_TSFT */
@@ -207,28 +236,35 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                 */
                *pos = 0;
        } else {
+               int shift = 0;
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = rate->bitrate / 5;
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               else if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
+               *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
        }
        pos++;
 
        /* IEEE80211_RADIOTAP_CHANNEL */
        put_unaligned_le16(status->freq, pos);
        pos += 2;
+       if (status->flag & RX_FLAG_10MHZ)
+               channel_flags |= IEEE80211_CHAN_HALF;
+       else if (status->flag & RX_FLAG_5MHZ)
+               channel_flags |= IEEE80211_CHAN_QUARTER;
+
        if (status->band == IEEE80211_BAND_5GHZ)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
        else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
-               put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
        else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else if (rate)
-               put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else
-               put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+               channel_flags |= IEEE80211_CHAN_2GHZ;
+       put_unaligned_le16(channel_flags, pos);
        pos += 2;
 
        /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -242,9 +278,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
 
-       /* IEEE80211_RADIOTAP_ANTENNA */
-       *pos = status->antenna;
-       pos++;
+       if (!status->chains) {
+               /* IEEE80211_RADIOTAP_ANTENNA */
+               *pos = status->antenna;
+               pos++;
+       }
 
        /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
@@ -341,6 +379,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                pos += 2;
        }
 
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               *pos++ = status->chain_signal[chain];
+               *pos++ = chain;
+       }
+
        if (status->vendor_radiotap_len) {
                /* ensure 2 byte alignment for the vendor field as required */
                if ((pos - (u8 *)rthdr) & 1)
index 1b122a7..08afe74 100644 (file)
@@ -66,6 +66,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen, srlen;
+       enum nl80211_bss_scan_width scan_width;
        s32 signal = 0;
 
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -73,8 +74,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
                signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-       cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
-                                        mgmt, len, signal, GFP_ATOMIC);
+       scan_width = NL80211_BSS_CHAN_WIDTH_20;
+       if (rx_status->flag & RX_FLAG_5MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_5;
+       if (rx_status->flag & RX_FLAG_10MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+       cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
+                                              scan_width, mgmt, len, signal,
+                                              GFP_ATOMIC);
        if (!cbss)
                return NULL;
 
@@ -204,10 +212,29 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
                ieee80211_rx_bss_put(local, bss);
 }
 
+static void
+ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
+                              enum nl80211_bss_scan_width scan_width)
+{
+       memset(chandef, 0, sizeof(*chandef));
+       switch (scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               chandef->width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               chandef->width = NL80211_CHAN_WIDTH_10;
+               break;
+       default:
+               chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
+}
+
 /* return false if no more work */
 static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
        struct cfg80211_scan_request *req = local->scan_req;
+       struct cfg80211_chan_def chandef;
        enum ieee80211_band band;
        int i, ielen, n_chans;
 
@@ -229,11 +256,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        } while (!n_chans);
 
        local->hw_scan_req->n_channels = n_chans;
+       ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
        ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
                                         local->hw_scan_ies_bufsize,
                                         req->ie, req->ie_len, band,
-                                        req->rates[band], 0);
+                                        req->rates[band], &chandef);
        local->hw_scan_req->ie_len = ielen;
        local->hw_scan_req->no_cck = req->no_cck;
 
@@ -280,7 +308,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        rcu_assign_pointer(local->scan_sdata, NULL);
 
        local->scanning = 0;
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
 
        /* Set power back to normal operating levels. */
        ieee80211_hw_config(local, 0);
@@ -615,11 +643,34 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
 {
        int skip;
        struct ieee80211_channel *chan;
+       enum nl80211_bss_scan_width oper_scan_width;
 
        skip = 0;
        chan = local->scan_req->channels[local->scan_channel_idx];
 
-       local->scan_channel = chan;
+       local->scan_chandef.chan = chan;
+       local->scan_chandef.center_freq1 = chan->center_freq;
+       local->scan_chandef.center_freq2 = 0;
+       switch (local->scan_req->scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_20:
+               /* If scanning on oper channel, use whatever channel-type
+                * is currently in use.
+                */
+               oper_scan_width = cfg80211_chandef_to_scan_width(
+                                       &local->_oper_chandef);
+               if (chan == local->_oper_chandef.chan &&
+                   oper_scan_width == local->scan_req->scan_width)
+                       local->scan_chandef = local->_oper_chandef;
+               else
+                       local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
 
        if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
                skip = 1;
@@ -659,7 +710,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
                                         unsigned long *next_delay)
 {
        /* switch back to the operating channel */
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        /* disable PS */
@@ -801,7 +852,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan)
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_local *local = sdata->local;
        int ret = -EBUSY;
@@ -851,6 +903,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 
        local->int_scan_req->ssids = &local->scan_ssid;
        local->int_scan_req->n_ssids = 1;
+       local->int_scan_req->scan_width = scan_width;
        memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
        local->int_scan_req->ssids[0].ssid_len = ssid_len;
 
@@ -912,6 +965,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sched_scan_ies sched_scan_ies = {};
+       struct cfg80211_chan_def chandef;
        int ret, i, iebufsz;
 
        iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
@@ -939,10 +993,12 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                        goto out_free;
                }
 
+               ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
                sched_scan_ies.len[i] =
                        ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
                                                 iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, 0);
+                                                i, (u32) -1, &chandef);
        }
 
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
index 4343920..6ad4c14 100644 (file)
@@ -252,9 +252,10 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
        return len;
 }
 
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
-                                            *sband, struct sk_buff *skb,
-                                            int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband,
+                                struct sk_buff *skb, int retry_count,
+                                int rtap_len, int shift)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -280,8 +281,11 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
        /* IEEE80211_RADIOTAP_RATE */
        if (info->status.rates[0].idx >= 0 &&
            !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+               u16 rate;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+               rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+               *pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
                /* padding for tx flags */
                pos += 2;
        }
@@ -424,6 +428,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        bool acked;
        struct ieee80211_bar *bar;
        int rtap_len;
+       int shift = 0;
 
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -458,6 +463,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
                        continue;
 
+               shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
                if (info->flags & IEEE80211_TX_STATUS_EOSP)
                        clear_sta_flag(sta, WLAN_STA_SP);
 
@@ -624,7 +631,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                dev_kfree_skb(skb);
                return;
        }
-       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len,
+                                        shift);
 
        /* XXX: is this sufficient for BPF? */
        skb_set_mac_header(skb, 0);
index 4105d0c..be4d3ca 100644 (file)
@@ -40,12 +40,22 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                                 struct sk_buff *skb, int group_addr,
                                 int next_frag_len)
 {
-       int rate, mrate, erp, dur, i;
+       int rate, mrate, erp, dur, i, shift = 0;
        struct ieee80211_rate *txrate;
        struct ieee80211_local *local = tx->local;
        struct ieee80211_supported_band *sband;
        struct ieee80211_hdr *hdr;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       u32 rate_flags = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
+       if (chanctx_conf) {
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+       }
+       rcu_read_unlock();
 
        /* assume HW handles this */
        if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@@ -122,8 +132,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                if (r->bitrate > txrate->bitrate)
                        break;
 
+               if ((rate_flags & r->flags) != rate_flags)
+                       continue;
+
                if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
-                       rate = r->bitrate;
+                       rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
                switch (sband->band) {
                case IEEE80211_BAND_2GHZ: {
@@ -150,7 +163,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        if (rate == -1) {
                /* No matching basic rate found; use highest suitable mandatory
                 * PHY rate */
-               rate = mrate;
+               rate = DIV_ROUND_UP(mrate, 1 << shift);
        }
 
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
@@ -162,7 +175,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
                 * to closest integer */
                dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
 
        if (next_frag_len) {
                /* Frame is fragmented: duration increases with time needed to
@@ -171,7 +185,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                /* next fragment */
                dur += ieee80211_frame_duration(sband->band, next_frag_len,
                                txrate->bitrate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
        }
 
        return cpu_to_le16(dur);
@@ -1257,6 +1272,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MONITOR:
+               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+                       vif = &sdata->vif;
+                       break;
+               }
                sdata = rcu_dereference(local->monitor_sdata);
                if (sdata) {
                        vif = &sdata->vif;
index 2265445..d23c5a7 100644 (file)
@@ -107,7 +107,8 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 }
 
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble)
+                            int rate, int erp, int short_preamble,
+                            int shift)
 {
        int dur;
 
@@ -118,6 +119,9 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
         *
         * rate is in 100 kbps, so divident is multiplied by 10 in the
         * DIV_ROUND_UP() operations.
+        *
+        * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
+        * is assumed to be 0 otherwise.
         */
 
        if (band == IEEE80211_BAND_5GHZ || erp) {
@@ -130,13 +134,23 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
                 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
                 *
                 * T_SYM = 4 usec
-                * 802.11a - 17.5.2: aSIFSTime = 16 usec
+                * 802.11a - 18.5.2: aSIFSTime = 16 usec
                 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
                 *      signal ext = 6 usec
                 */
                dur = 16; /* SIFS + signal ext */
-               dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
-               dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+               dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
+               dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
+
+               /* IEEE 802.11-2012 18.3.2.4: all values above are:
+                *  * times 4 for 5 MHz
+                *  * times 2 for 10 MHz
+                */
+               dur *= 1 << shift;
+
+               /* rates should already consider the channel bandwidth,
+                * don't apply divisor again.
+                */
                dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
                                        4 * rate); /* T_SYM x N_SYM */
        } else {
@@ -168,7 +182,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_sub_if_data *sdata;
        u16 dur;
-       int erp;
+       int erp, shift = 0;
        bool short_preamble = false;
 
        erp = 0;
@@ -177,10 +191,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
        dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
-                                      short_preamble);
+                                      short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -194,7 +209,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -210,17 +225,20 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* CTS duration */
-       dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, 10, bitrate,
+                                      erp, short_preamble, shift);
        /* Data frame duration */
-       dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                       erp, short_preamble, shift);
        /* ACK duration */
-       dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                       erp, short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -235,7 +253,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -250,15 +268,18 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* Data frame duration */
-       dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                      erp, short_preamble, shift);
        if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
                /* ACK duration */
-               dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                               erp, short_preamble);
+               dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                               erp, short_preamble, shift);
        }
 
        return cpu_to_le16(dur);
@@ -1052,32 +1073,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        }
 }
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates)
-{
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       int i, have_higher_than_11mbit = 0;
-
-       /* cf. IEEE 802.11 9.2.12 */
-       for (i = 0; i < supp_rates_len; i++)
-               if ((supp_rates[i] & 0x7f) * 5 > 110)
-                       have_higher_than_11mbit = 1;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
-       if (chanctx_conf &&
-           chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
-           have_higher_than_11mbit)
-               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-       else
-               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-       rcu_read_unlock();
-
-       ieee80211_set_wmm_default(sdata, true);
-}
-
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *da,
@@ -1162,7 +1157,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel)
+                            struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
@@ -1171,16 +1166,26 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
        u8 rates[32];
        int num_rates;
        int ext_rates_len;
+       int shift;
+       u32 rate_flags;
 
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
 
+       rate_flags = ieee80211_chandef_rate_flags(chandef);
+       shift = ieee80211_chandef_get_shift(chandef);
+
        num_rates = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if ((BIT(i) & rate_mask) == 0)
                        continue; /* skip rate */
-               rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
+               rates[num_rates++] =
+                       (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                         (1 << shift) * 5);
        }
 
        supp_rates_len = min_t(int, num_rates, 8);
@@ -1220,12 +1225,13 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                pos += ext_rates_len;
        }
 
-       if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+       if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
                if (end - pos < 3)
                        goto out_err;
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
-               *pos++ = channel;
+               *pos++ = ieee80211_frequency_to_channel(
+                               chandef->chan->center_freq);
        }
 
        /* insert custom IEs that go before HT */
@@ -1290,9 +1296,9 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          bool directed)
 {
        struct ieee80211_local *local = sdata->local;
+       struct cfg80211_chan_def chandef;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 chan_no;
        int ies_len;
 
        /*
@@ -1300,10 +1306,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
         * in order to maximize the chance that we get a response.  Some
         * badly-behaved APs don't respond when this parameter is included.
         */
+       chandef.width = sdata->vif.bss_conf.chandef.width;
        if (directed)
-               chan_no = 0;
+               chandef.chan = NULL;
        else
-               chan_no = ieee80211_frequency_to_channel(chan->center_freq);
+               chandef.chan = chan;
 
        skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
                                     ssid, ssid_len, 100 + ie_len);
@@ -1313,7 +1320,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
                                           skb_tailroom(skb),
                                           ie, ie_len, chan->band,
-                                          ratemask, chan_no);
+                                          ratemask, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {
@@ -1347,16 +1354,19 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        }
 }
 
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates)
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *bitrates;
        size_t num_rates;
-       u32 supp_rates;
-       int i, j;
-       sband = local->hw.wiphy->bands[band];
+       u32 supp_rates, rate_flags;
+       int i, j, shift;
+       sband = sdata->local->hw.wiphy->bands[band];
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (WARN_ON(!sband))
                return 1;
@@ -1381,7 +1391,15 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
                        continue;
 
                for (j = 0; j < num_rates; j++) {
-                       if (bitrates[j].bitrate == own_rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            1 << shift);
+
+                       if (brate == own_rate) {
                                supp_rates |= BIT(j);
                                if (basic_rates && is_basic)
                                        *basic_rates |= BIT(j);
@@ -2004,18 +2022,56 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
        cfg80211_chandef_create(chandef, control_chan, channel_type);
 }
 
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates)
+{
+       u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
+       int shift = ieee80211_chandef_get_shift(chandef);
+       struct ieee80211_rate *br;
+       int brate, rate, i, j, count = 0;
+
+       *rates = 0;
+
+       for (i = 0; i < srates_len; i++) {
+               rate = srates[i] & 0x7f;
+
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
+                               *rates |= BIT(j);
+                               count++;
+                               break;
+                       }
+               }
+       }
+       return count;
+}
+
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, shift;
        u8 i, rates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
 
+       shift = ieee80211_vif_get_shift(&sdata->vif);
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
        sband = local->hw.wiphy->bands[band];
-       rates = sband->n_bitrates;
+       rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               rates++;
+       }
        if (rates > 8)
                rates = 8;
 
@@ -2027,10 +2083,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = rates;
        for (i = 0; i < rates; i++) {
                u8 basic = 0;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                if (need_basic && basic_rates & BIT(i))
                        basic = 0x80;
                rate = sband->bitrates[i].bitrate;
-               *pos++ = basic | (u8) (rate / 5);
+               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                   5 * (1 << shift));
+               *pos++ = basic | (u8) rate;
        }
 
        return 0;
@@ -2042,12 +2103,22 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, skip, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        sband = local->hw.wiphy->bands[band];
-       exrates = sband->n_bitrates;
+       exrates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               exrates++;
+       }
+
        if (exrates > 8)
                exrates -= 8;
        else
@@ -2060,12 +2131,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
+               skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       if (skip++ < 8)
+                               continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
-                       rate = sband->bitrates[i].bitrate;
-                       *pos++ = basic | (u8) (rate / 5);
+                       rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                           5 * (1 << shift));
+                       *pos++ = basic | (u8) rate;
                }
        }
        return 0;
@@ -2149,9 +2227,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = 0;
+               int bitrate;
+
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
 
                sband = local->hw.wiphy->bands[status->band];
-               ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+               bitrate = sband->bitrates[status->rate_idx].bitrate;
+               ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
index a8c29fa..6715396 100644 (file)
@@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy)
                return -EINVAL;
 #endif
 
+       if (WARN_ON(wiphy->coalesce &&
+                   (!wiphy->coalesce->n_rules ||
+                    !wiphy->coalesce->n_patterns) &&
+                   (!wiphy->coalesce->pattern_min_len ||
+                    wiphy->coalesce->pattern_min_len >
+                       wiphy->coalesce->pattern_max_len)))
+               return -EINVAL;
+
        if (WARN_ON(wiphy->ap_sme_capa &&
                    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
                return -EINVAL;
@@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy)
                rdev_set_wakeup(rdev, false);
 #endif
        cfg80211_rdev_free_wowlan(rdev);
+       cfg80211_rdev_free_coalesce(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
index a6b45bf..9ad43c6 100644 (file)
@@ -79,6 +79,8 @@ struct cfg80211_registered_device {
        /* netlink port which started critical protocol (0 means not started) */
        u32 crit_proto_nlportid;
 
+       struct cfg80211_coalesce *coalesce;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
index 30c4920..0553fd4 100644 (file)
@@ -167,9 +167,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
         * basic rates
         */
        if (!setup->basic_rates) {
+               enum nl80211_bss_scan_width scan_width;
                struct ieee80211_supported_band *sband =
                                rdev->wiphy.bands[setup->chandef.chan->band];
-               setup->basic_rates = ieee80211_mandatory_rates(sband);
+               scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+               setup->basic_rates = ieee80211_mandatory_rates(sband,
+                                                              scan_width);
        }
 
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
index 3fcba69..587ff84 100644 (file)
@@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
        [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
 };
 
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+       [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
 /* policy for GTK rekey offload attributes */
 static const struct nla_policy
 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@ -976,7 +984,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                return -ENOBUFS;
 
        if (dev->wiphy.wowlan->n_patterns) {
-               struct nl80211_wowlan_pattern_support pat = {
+               struct nl80211_pattern_support pat = {
                        .max_patterns = dev->wiphy.wowlan->n_patterns,
                        .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
                        .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
@@ -997,6 +1005,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 }
 #endif
 
+static int nl80211_send_coalesce(struct sk_buff *msg,
+                                struct cfg80211_registered_device *dev)
+{
+       struct nl80211_coalesce_rule_support rule;
+
+       if (!dev->wiphy.coalesce)
+               return 0;
+
+       rule.max_rules = dev->wiphy.coalesce->n_rules;
+       rule.max_delay = dev->wiphy.coalesce->max_delay;
+       rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
+       rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
+       rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
+       rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+
+       if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+               return -ENOBUFS;
+
+       return 0;
+}
+
 static int nl80211_send_band_rateinfo(struct sk_buff *msg,
                                      struct ieee80211_supported_band *sband)
 {
@@ -1515,6 +1544,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                            dev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
 
+               state->split_start++;
+               break;
+       case 10:
+               if (nl80211_send_coalesce(msg, dev))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -5641,6 +5676,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                goto nla_put_failure;
        if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
            nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+           nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
            nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
@@ -6321,6 +6357,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        switch (ibss.chandef.width) {
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
@@ -6348,6 +6386,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               memcpy(&ibss.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(ibss.ht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&ibss.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(ibss.ht_capa));
+       }
+
        if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
            !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
                        nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@@ -7595,12 +7646,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                if (!nl_pat)
                        return -ENOBUFS;
                pat_len = wowlan->patterns[i].pattern_len;
-               if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
-                           DIV_ROUND_UP(pat_len, 8),
+               if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
                            wowlan->patterns[i].mask) ||
-                   nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-                           pat_len, wowlan->patterns[i].pattern) ||
-                   nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
+                   nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                           wowlan->patterns[i].pattern) ||
+                   nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
                                wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
@@ -7941,7 +7991,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                struct nlattr *pat;
                int n_patterns = 0;
                int rem, pat_len, mask_len, pkt_offset;
-               struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+               struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
@@ -7960,26 +8010,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
-                       nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
-                                 nla_data(pat), nla_len(pat), NULL);
+                       nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                                 nla_len(pat), NULL);
                        err = -EINVAL;
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
-                           !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+                       if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                           !pat_tb[NL80211_PKTPAT_PATTERN])
                                goto error;
-                       pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+                       pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
                        mask_len = DIV_ROUND_UP(pat_len, 8);
-                       if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
-                           mask_len)
+                       if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
                                goto error;
                        if (pat_len > wowlan->pattern_max_len ||
                            pat_len < wowlan->pattern_min_len)
                                goto error;
 
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
+                       if (!pat_tb[NL80211_PKTPAT_OFFSET])
                                pkt_offset = 0;
                        else
                                pkt_offset = nla_get_u32(
-                                       pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
+                                       pat_tb[NL80211_PKTPAT_OFFSET]);
                        if (pkt_offset > wowlan->max_pkt_offset)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
@@ -7993,11 +8042,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        new_triggers.patterns[i].pattern =
                                new_triggers.patterns[i].mask + mask_len;
                        memcpy(new_triggers.patterns[i].mask,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+                              nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
                        new_triggers.patterns[i].pattern_len = pat_len;
                        memcpy(new_triggers.patterns[i].pattern,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+                              nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
                }
@@ -8036,6 +8085,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 }
 #endif
 
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+                                      struct cfg80211_registered_device *rdev)
+{
+       struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+       int i, j, pat_len;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!rdev->coalesce->n_rules)
+               return 0;
+
+       nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+       if (!nl_rules)
+               return -ENOBUFS;
+
+       for (i = 0; i < rdev->coalesce->n_rules; i++) {
+               nl_rule = nla_nest_start(msg, i + 1);
+               if (!nl_rule)
+                       return -ENOBUFS;
+
+               rule = &rdev->coalesce->rules[i];
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+                               rule->delay))
+                       return -ENOBUFS;
+
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+                               rule->condition))
+                       return -ENOBUFS;
+
+               nl_pats = nla_nest_start(msg,
+                               NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+               if (!nl_pats)
+                       return -ENOBUFS;
+
+               for (j = 0; j < rule->n_patterns; j++) {
+                       nl_pat = nla_nest_start(msg, j + 1);
+                       if (!nl_pat)
+                               return -ENOBUFS;
+                       pat_len = rule->patterns[j].pattern_len;
+                       if (nla_put(msg, NL80211_PKTPAT_MASK,
+                                   DIV_ROUND_UP(pat_len, 8),
+                                   rule->patterns[j].mask) ||
+                           nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                                   rule->patterns[j].pattern) ||
+                           nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+                                       rule->patterns[j].pkt_offset))
+                               return -ENOBUFS;
+                       nla_nest_end(msg, nl_pat);
+               }
+               nla_nest_end(msg, nl_pats);
+               nla_nest_end(msg, nl_rule);
+       }
+       nla_nest_end(msg, nl_rules);
+
+       return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct sk_buff *msg;
+       void *hdr;
+
+       if (!rdev->wiphy.coalesce)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_COALESCE);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_coalesce *coalesce = rdev->coalesce;
+       int i, j;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!coalesce)
+               return;
+
+       for (i = 0; i < coalesce->n_rules; i++) {
+               rule = &coalesce->rules[i];
+               for (j = 0; j < rule->n_patterns; j++)
+                       kfree(rule->patterns[j].mask);
+               kfree(rule->patterns);
+       }
+       kfree(coalesce->rules);
+       kfree(coalesce);
+       rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+                                      struct nlattr *rule,
+                                      struct cfg80211_coalesce_rules *new_rule)
+{
+       int err, i;
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+       int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+       struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+       err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
+                       nla_len(rule), nl80211_coalesce_policy);
+       if (err)
+               return err;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+               new_rule->delay =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+       if (new_rule->delay > coalesce->max_delay)
+               return -EINVAL;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+               new_rule->condition =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+       if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+           new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+               return -EINVAL;
+
+       if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+               return -EINVAL;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem)
+               n_patterns++;
+       if (n_patterns > coalesce->n_patterns)
+               return -EINVAL;
+
+       new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+                                    GFP_KERNEL);
+       if (!new_rule->patterns)
+               return -ENOMEM;
+
+       new_rule->n_patterns = n_patterns;
+       i = 0;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem) {
+               nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                         nla_len(pat), NULL);
+               if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                   !pat_tb[NL80211_PKTPAT_PATTERN])
+                       return -EINVAL;
+               pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+               mask_len = DIV_ROUND_UP(pat_len, 8);
+               if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+                       return -EINVAL;
+               if (pat_len > coalesce->pattern_max_len ||
+                   pat_len < coalesce->pattern_min_len)
+                       return -EINVAL;
+
+               if (!pat_tb[NL80211_PKTPAT_OFFSET])
+                       pkt_offset = 0;
+               else
+                       pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+               if (pkt_offset > coalesce->max_pkt_offset)
+                       return -EINVAL;
+               new_rule->patterns[i].pkt_offset = pkt_offset;
+
+               new_rule->patterns[i].mask =
+                       kmalloc(mask_len + pat_len, GFP_KERNEL);
+               if (!new_rule->patterns[i].mask)
+                       return -ENOMEM;
+               new_rule->patterns[i].pattern =
+                       new_rule->patterns[i].mask + mask_len;
+               memcpy(new_rule->patterns[i].mask,
+                      nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+               new_rule->patterns[i].pattern_len = pat_len;
+               memcpy(new_rule->patterns[i].pattern,
+                      nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+               i++;
+       }
+
+       return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct cfg80211_coalesce new_coalesce = {};
+       struct cfg80211_coalesce *n_coalesce;
+       int err, rem_rule, n_rules = 0, i, j;
+       struct nlattr *rule;
+       struct cfg80211_coalesce_rules *tmp_rule;
+
+       if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+               cfg80211_rdev_free_coalesce(rdev);
+               rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+               return 0;
+       }
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule)
+               n_rules++;
+       if (n_rules > coalesce->n_rules)
+               return -EINVAL;
+
+       new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+                                    GFP_KERNEL);
+       if (!new_coalesce.rules)
+               return -ENOMEM;
+
+       new_coalesce.n_rules = n_rules;
+       i = 0;
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule) {
+               err = nl80211_parse_coalesce_rule(rdev, rule,
+                                                 &new_coalesce.rules[i]);
+               if (err)
+                       goto error;
+
+               i++;
+       }
+
+       err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+       if (err)
+               goto error;
+
+       n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+       if (!n_coalesce) {
+               err = -ENOMEM;
+               goto error;
+       }
+       cfg80211_rdev_free_coalesce(rdev);
+       rdev->coalesce = n_coalesce;
+
+       return 0;
+error:
+       for (i = 0; i < new_coalesce.n_rules; i++) {
+               tmp_rule = &new_coalesce.rules[i];
+               for (j = 0; j < tmp_rule->n_patterns; j++)
+                       kfree(tmp_rule->patterns[j].mask);
+               kfree(tmp_rule->patterns);
+       }
+       kfree(new_coalesce.rules);
+
+       return err;
+}
+
 static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9043,6 +9350,21 @@ static struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_GET_COALESCE,
+               .doit = nl80211_get_coalesce,
+               .policy = nl80211_policy,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_SET_COALESCE,
+               .doit = nl80211_set_coalesce,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
        }
 };
 
index a4073e8..44341bf 100644 (file)
@@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
 
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index ae8c186..ad1e406 100644 (file)
@@ -651,6 +651,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
                        continue;
                if (bss->pub.channel != new->pub.channel)
                        continue;
+               if (bss->pub.scan_width != new->pub.scan_width)
+                       continue;
                if (rcu_access_pointer(bss->pub.beacon_ies))
                        continue;
                ies = rcu_access_pointer(bss->pub.ies);
@@ -870,11 +872,12 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss*
-cfg80211_inform_bss(struct wiphy *wiphy,
-                   struct ieee80211_channel *channel,
-                   const u8 *bssid, u64 tsf, u16 capability,
-                   u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp)
 {
        struct cfg80211_bss_ies *ies;
        struct cfg80211_internal_bss tmp = {}, *res;
@@ -892,6 +895,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
 
        memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = beacon_interval;
        tmp.pub.capability = capability;
@@ -924,14 +928,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss);
+EXPORT_SYMBOL(cfg80211_inform_bss_width);
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
-                         struct ieee80211_channel *channel,
-                         struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp)
 {
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
@@ -941,7 +946,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-       trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
+       trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+                                             len, signal);
 
        if (WARN_ON(!mgmt))
                return NULL;
@@ -976,6 +982,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        
        memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@@ -991,7 +998,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
index e1534ba..09af6eb 100644 (file)
@@ -2391,26 +2391,30 @@ TRACE_EVENT(cfg80211_get_bss,
                  __entry->capa_mask, __entry->capa_val)
 );
 
-TRACE_EVENT(cfg80211_inform_bss_frame,
+TRACE_EVENT(cfg80211_inform_bss_width_frame,
        TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+                enum nl80211_bss_scan_width scan_width,
                 struct ieee80211_mgmt *mgmt, size_t len,
                 s32 signal),
-       TP_ARGS(wiphy, channel, mgmt, len, signal),
+       TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_ENTRY
+               __field(enum nl80211_bss_scan_width, scan_width)
                __dynamic_array(u8, mgmt, len)
                __field(s32, signal)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                CHAN_ASSIGN(channel);
+               __entry->scan_width = scan_width;
                if (mgmt)
                        memcpy(__get_dynamic_array(mgmt), mgmt, len);
                __entry->signal = signal;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
-                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+                 __entry->signal)
 );
 
 DECLARE_EVENT_CLASS(cfg80211_bss_evt,
index 74458b7..ce090c1 100644 (file)
@@ -33,7 +33,8 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 }
 EXPORT_SYMBOL(ieee80211_get_response_rate);
 
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_rate *bitrates;
        u32 mandatory_rates = 0;
@@ -43,10 +44,15 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
        if (WARN_ON(!sband))
                return 1;
 
-       if (sband->band == IEEE80211_BAND_2GHZ)
-               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
-       else
+       if (sband->band == IEEE80211_BAND_2GHZ) {
+               if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+                   scan_width == NL80211_BSS_CHAN_WIDTH_10)
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_G;
+               else
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+       } else {
                mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+       }
 
        bitrates = sband->bitrates;
        for (i = 0; i < sband->n_bitrates; i++)