Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 22 Jul 2014 17:50:23 +0000 (13:50 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 22 Jul 2014 17:50:23 +0000 (13:50 -0400)
162 files changed:
drivers/bcma/Makefile
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/driver_pcie2.c [new file with mode: 0644]
drivers/bcma/host_pci.c
drivers/bcma/main.c
drivers/bcma/sprom.c
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/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
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/ath6kl/bmi.h
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/htc_pipe.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h
drivers/net/wireless/ath/ath9k/ar9002_mac.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/spectral.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/main.h
drivers/net/wireless/b43/phy_lcn.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/phy_n.h
drivers/net/wireless/b43/radio_2057.c
drivers/net/wireless/b43/radio_2057.h
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/b43/tables_nphy.h
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/chip.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/feature.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/feature.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/brcm80211/brcmfmac/firmware.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/coex_legacy.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.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/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
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/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
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/trans.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl18xx/cmd.c
drivers/net/wireless/ti/wl18xx/cmd.h
drivers/net/wireless/ti/wl18xx/event.c
drivers/net/wireless/ti/wl18xx/event.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/vendor_cmd.c [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/vendor_cmd.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/net/wireless/zd1211rw/Kconfig
drivers/ssb/pci.c
include/linux/bcma/bcma.h
include/linux/bcma/bcma_driver_pcie2.h [new file with mode: 0644]
include/linux/ieee80211.h
include/linux/ssb/ssb_regs.h
include/net/mac80211.h
net/mac80211/agg-rx.c
net/mac80211/chan.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.h
net/mac80211/tdls.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/wireless/Kconfig
net/wireless/core.h
net/wireless/genregdb.awk
net/wireless/nl80211.c
net/wireless/reg.c

index 734b32f09c0a21aa7ef734100400a47f13f485ad..91290f7f61b8c2b903d874345b6c32896da6114a 100644 (file)
@@ -3,6 +3,7 @@ bcma-y                                  += driver_chipcommon.o driver_chipcommon_pmu.o
 bcma-$(CONFIG_BCMA_SFLASH)             += driver_chipcommon_sflash.o
 bcma-$(CONFIG_BCMA_NFLASH)             += driver_chipcommon_nflash.o
 bcma-y                                 += driver_pci.o
+bcma-y                                 += driver_pcie2.o
 bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)        += driver_pci_host.o
 bcma-$(CONFIG_BCMA_DRIVER_MIPS)                += driver_mips.o
 bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN)    += driver_gmac_cmn.o
index 5081a8c439ccd12707bb32b332b42d1fd38bdf7c..bb694e2e9f32917358e676d9f7203fe5d876c80c 100644 (file)
@@ -603,6 +603,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW;
                break;
 
+       case BCMA_CHIP_ID_BCM43217:
        case BCMA_CHIP_ID_BCM43227:
        case BCMA_CHIP_ID_BCM43228:
        case BCMA_CHIP_ID_BCM43428:
diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c
new file mode 100644 (file)
index 0000000..e4be537
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Broadcom specific AMBA
+ * PCIe Gen 2 Core
+ *
+ * Copyright 2014, Broadcom Corporation
+ * Copyright 2014, RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+#if 0
+static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
+       pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR);
+       return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
+}
+#endif
+
+static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr,
+                                     u32 val)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val);
+}
+
+/**************************************************
+ * Init.
+ **************************************************/
+
+static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2,
+                                               bool enable)
+{
+       u32 val;
+
+       /* restore back to default */
+       val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
+       val |= PCIE2_CLKC_DLYPERST;
+       val &= ~PCIE2_CLKC_DISSPROMLD;
+       if (enable) {
+               val &= ~PCIE2_CLKC_DLYPERST;
+               val |= PCIE2_CLKC_DISSPROMLD;
+       }
+       pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val);
+       /* flush */
+       return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
+}
+
+static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2)
+{
+       /* LTR0 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c);
+       /* LTR1 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864);
+       /* LTR2 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003);
+}
+
+static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2)
+{
+       u8 core_rev = pcie2->core->id.rev;
+       u32 devstsctr2;
+
+       if (core_rev < 2 || core_rev == 10 || core_rev > 13)
+               return;
+
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                     PCIE2_CAP_DEVSTSCTRL2_OFFSET);
+       devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
+       if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) {
+               /* force the right LTR values */
+               bcma_core_pcie2_set_ltr_vals(pcie2);
+
+               /* TODO:
+               si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */
+
+               /* enable the LTR */
+               devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB;
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                             PCIE2_CAP_DEVSTSCTRL2_OFFSET);
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2);
+
+               /* set the LTR state to be active */
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
+                             PCIE2_LTR_ACTIVE);
+               usleep_range(1000, 2000);
+
+               /* set the LTR state to be sleep */
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
+                             PCIE2_LTR_SLEEP);
+               usleep_range(1000, 2000);
+       }
+}
+
+static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2)
+{
+       u8 core_rev = pcie2->core->id.rev;
+       bool pciewar160, pciewar162;
+
+       pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11;
+       pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 ||
+                    core_rev == 9 || core_rev == 11;
+
+       if (!pciewar160 && !pciewar162)
+               return;
+
+/* TODO */
+#if 0
+       pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL,
+                   PCIE_DISABLE_L1CLK_GATING);
+#if 0
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                     PCIEGEN2_COE_PVT_TL_CTRL_0);
+       pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA,
+                    ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT));
+#endif
+#endif
+}
+
+static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP);
+       pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f);
+}
+
+static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0);
+}
+
+static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2)
+{
+       struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc;
+       u8 core_rev = pcie2->core->id.rev;
+       u32 alp_khz, pm_value;
+
+       if (core_rev <= 13) {
+               alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000;
+               pm_value = (1000000 * 2) / alp_khz;
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                             PCIE2_PVT_REG_PM_CLK_PERIOD);
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value);
+       }
+}
+
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
+{
+       struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo;
+       u32 tmp;
+
+       tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54));
+       if ((tmp & 0xe) >> 1 == 2)
+               bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17);
+
+       /* TODO: Do we need pcie_reqsize? */
+
+       if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3)
+               bcma_core_pcie2_war_delay_perst_enab(pcie2, true);
+       bcma_core_pcie2_hw_ltr_war(pcie2);
+       pciedev_crwlpciegen2(pcie2);
+       pciedev_reg_pm_clk_period(pcie2);
+       pciedev_crwlpciegen2_180(pcie2);
+       pciedev_crwlpciegen2_182(pcie2);
+}
index e333305363aa0491b9ce7fef3473e1a2cbbf1e07..3cf725a49dc184e83951e44628ae80b75654bed4 100644 (file)
@@ -279,6 +279,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
        { 0, },
 };
index 34ea4c588d36bd798ee83bc9db6273eba6baf034..0ff8d58831ef30fcbe7a5452baa8b00e3fdababa 100644 (file)
@@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
                case BCMA_CORE_CHIPCOMMON:
                case BCMA_CORE_PCI:
                case BCMA_CORE_PCIE:
+               case BCMA_CORE_PCIE2:
                case BCMA_CORE_MIPS_74K:
                case BCMA_CORE_4706_MAC_GBIT_COMMON:
                        continue;
@@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus)
                bcma_core_pci_init(&bus->drv_pci[1]);
        }
 
+       /* Init PCIe Gen 2 core */
+       core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0);
+       if (core) {
+               bus->drv_pcie2.core = core;
+               bcma_core_pcie2_init(&bus->drv_pcie2);
+       }
+
        /* Init GBIT MAC COMMON core */
        core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
        if (core) {
index 72bf4540f5658425b4836303d8ed6d95b43a6702..97bb38e9ed653ba0aa6712333705d860b114611d 100644 (file)
@@ -201,6 +201,23 @@ static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
                SPEX(_field[7], _offset + 14, _mask, _shift);   \
        } while (0)
 
+static s8 sprom_extract_antgain(const u16 *in, u16 offset, u16 mask, u16 shift)
+{
+       u16 v;
+       u8 gain;
+
+       v = in[SPOFF(offset)];
+       gain = (v & mask) >> shift;
+       if (gain == 0xFF) {
+               gain = 8; /* If unset use 2dBm */
+       } else {
+               /* Q5.2 Fractional part is stored in 0xC0 */
+               gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
+       }
+
+       return (s8)gain;
+}
+
 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
 {
        u16 v, o;
@@ -381,14 +398,22 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
        SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
+       bus->sprom.antenna_gain.a0 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN01,
+                                                          SSB_SPROM8_AGAIN0,
+                                                          SSB_SPROM8_AGAIN0_SHIFT);
+       bus->sprom.antenna_gain.a1 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN01,
+                                                          SSB_SPROM8_AGAIN1,
+                                                          SSB_SPROM8_AGAIN1_SHIFT);
+       bus->sprom.antenna_gain.a2 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN23,
+                                                          SSB_SPROM8_AGAIN2,
+                                                          SSB_SPROM8_AGAIN2_SHIFT);
+       bus->sprom.antenna_gain.a3 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN23,
+                                                          SSB_SPROM8_AGAIN3,
+                                                          SSB_SPROM8_AGAIN3_SHIFT);
 
        SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
             SSB_SPROM8_LEDDC_ON_SHIFT);
@@ -509,6 +534,7 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
                /* for these chips OTP is always available */
                present = true;
                break;
+       case BCMA_CHIP_ID_BCM43217:
        case BCMA_CHIP_ID_BCM43227:
        case BCMA_CHIP_ID_BCM43228:
        case BCMA_CHIP_ID_BCM43428:
index d185dc0cd12b2f739535a6bc6fe77020b0764133..4333107ecf37b8325c55f31569361a9b537dddc6 100644 (file)
@@ -603,16 +603,19 @@ static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
                if (ret)
                        return ret;
 
-               src_ring->hw_index =
-                       ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
-               src_ring->hw_index &= nentries_mask;
+               read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               if (read_index == 0xffffffff)
+                       return -ENODEV;
+
+               read_index &= nentries_mask;
+               src_ring->hw_index = read_index;
 
                ath10k_pci_sleep(ar);
        }
 
        read_index = src_ring->hw_index;
 
-       if ((read_index == sw_index) || (read_index == 0xffffffff))
+       if (read_index == sw_index)
                return -EIO;
 
        sbase = src_ring->shadow_base;
index 82017f56e6613a484b224a2d9d41f8aa796f16c9..93adb8c5896926c787bb80e6c0869cd7f5fd4672 100644 (file)
@@ -795,10 +795,14 @@ int ath10k_core_start(struct ath10k *ar)
        if (status)
                goto err_htc_stop;
 
-       ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1;
+       else
+               ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+
        INIT_LIST_HEAD(&ar->arvifs);
 
-       if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+       if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) {
                ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
                            ar->hw_params.name,
                            ar->target_version,
@@ -807,6 +811,12 @@ int ath10k_core_start(struct ath10k *ar)
                            ar->fw_api,
                            ar->htt.target_version_major,
                            ar->htt.target_version_minor);
+               ath10k_info("debug %d debugfs %d tracing %d dfs %d\n",
+                           config_enabled(CONFIG_ATH10K_DEBUG),
+                           config_enabled(CONFIG_ATH10K_DEBUGFS),
+                           config_enabled(CONFIG_ATH10K_TRACING),
+                           config_enabled(CONFIG_ATH10K_DFS_CERTIFIED));
+       }
 
        __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags);
 
@@ -984,7 +994,9 @@ err_unregister_mac:
 err_release_fw:
        ath10k_core_free_firmware_files(ar);
 err:
-       device_release_driver(ar->dev);
+       /* TODO: It's probably a good idea to release device from the driver
+        * but calling device_release_driver() here will cause a deadlock.
+        */
        return;
 }
 
index 68ceef61933db6b769122ab2500fa524619ac465..83a5fa91531d159b9095ef0fff37db55c38e0eeb 100644 (file)
@@ -290,6 +290,9 @@ struct ath10k_debug {
        struct ath_dfs_pool_stats dfs_pool_stats;
 
        u32 fw_dbglog_mask;
+
+       u8 htt_max_amsdu;
+       u8 htt_max_ampdu;
 };
 
 enum ath10k_state {
index 1b7ff4ba122ce42af61265eae30d8fb98d97201e..3030158c478e86cd9ee193af14b29da614f0d168 100644 (file)
@@ -671,6 +671,72 @@ static const struct file_operations fops_htt_stats_mask = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char buf[64];
+       u8 amsdu = 3, ampdu = 64;
+       unsigned int len;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->debug.htt_max_amsdu)
+               amsdu = ar->debug.htt_max_amsdu;
+
+       if (ar->debug.htt_max_ampdu)
+               ampdu = ar->debug.htt_max_ampdu;
+
+       mutex_unlock(&ar->conf_mutex);
+
+       len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       int res;
+       char buf[64];
+       unsigned int amsdu, ampdu;
+
+       simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+       /* make sure that buf is null terminated */
+       buf[sizeof(buf) - 1] = 0;
+
+       res = sscanf(buf, "%u %u", &amsdu, &ampdu);
+
+       if (res != 2)
+               return -EINVAL;
+
+       mutex_lock(&ar->conf_mutex);
+
+       res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);
+       if (res)
+               goto out;
+
+       res = count;
+       ar->debug.htt_max_amsdu = amsdu;
+       ar->debug.htt_max_ampdu = ampdu;
+
+out:
+       mutex_unlock(&ar->conf_mutex);
+       return res;
+}
+
+static const struct file_operations fops_htt_max_amsdu_ampdu = {
+       .read = ath10k_read_htt_max_amsdu_ampdu,
+       .write = ath10k_write_htt_max_amsdu_ampdu,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static ssize_t ath10k_read_fw_dbglog(struct file *file,
                                            char __user *user_buf,
                                            size_t count, loff_t *ppos)
@@ -757,6 +823,9 @@ void ath10k_debug_stop(struct ath10k *ar)
         * warning from del_timer(). */
        if (ar->debug.htt_stats_mask != 0)
                cancel_delayed_work(&ar->debug.htt_stats_dwork);
+
+       ar->debug.htt_max_amsdu = 0;
+       ar->debug.htt_max_ampdu = 0;
 }
 
 static ssize_t ath10k_write_simulate_radar(struct file *file,
@@ -867,6 +936,10 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_htt_stats_mask);
 
+       debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR,
+                           ar->debug.debugfs_phy, ar,
+                           &fops_htt_max_amsdu_ampdu);
+
        debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_fw_dbglog);
 
index 9a263462c79353fa4b6a3c646ff6d025087c9463..6c93f3885ee571097eeb59f59e9d72e772c97ab7 100644 (file)
@@ -240,16 +240,10 @@ struct htt_oob_sync_req {
        __le16 rsvd0;
 } __packed;
 
-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
-
 struct htt_aggr_conf {
        u8 max_num_ampdu_subframes;
-       union {
-               /* dont use bitfields; undefined behaviour */
-               u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
-               u8 max_num_amsdu_subframes:5;
-       } __packed;
+       /* amsdu_subframes is limited by 0x1F mask */
+       u8 max_num_amsdu_subframes;
 } __packed;
 
 #define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
@@ -1343,6 +1337,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
 int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
 int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
+                               u8 max_subfrms_ampdu,
+                               u8 max_subfrms_amsdu);
 
 void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
 int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
index 6c102b1312ff955db686022aa76e1a7ccc6e42b3..eebc860c36550a4ae65bb3910d799a86c0e8231a 100644 (file)
@@ -312,7 +312,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        int msdu_len, msdu_chaining = 0;
        struct sk_buff *msdu;
        struct htt_rx_desc *rx_desc;
-       bool corrupted = false;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
@@ -439,9 +438,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
                                RX_MSDU_END_INFO0_LAST_MSDU;
 
-               if (msdu_chaining && !last_msdu)
-                       corrupted = true;
-
                if (last_msdu) {
                        msdu->next = NULL;
                        break;
@@ -456,20 +452,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        if (*head_msdu == NULL)
                msdu_chaining = -1;
 
-       /*
-        * Apparently FW sometimes reports weird chained MSDU sequences with
-        * more than one rx descriptor. This seems like a bug but needs more
-        * analyzing. For the time being fix it by dropping such sequences to
-        * avoid blowing up the host system.
-        */
-       if (corrupted) {
-               ath10k_warn("failed to pop chained msdus, dropping\n");
-               ath10k_htt_rx_free_msdu_chain(*head_msdu);
-               *head_msdu = NULL;
-               *tail_msdu = NULL;
-               msdu_chaining = -EINVAL;
-       }
-
        /*
         * Don't refill the ring yet.
         *
index 7064354d1f4f0aa82f4624a7d878dea220aa4b2a..accb6b4f6fafedea2a2102b5c43c39e4835840b8 100644 (file)
@@ -307,6 +307,52 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
        return 0;
 }
 
+int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
+                               u8 max_subfrms_ampdu,
+                               u8 max_subfrms_amsdu)
+{
+       struct htt_aggr_conf *aggr_conf;
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len;
+       int ret;
+
+       /* Firmware defaults are: amsdu = 3 and ampdu = 64 */
+
+       if (max_subfrms_ampdu == 0 || max_subfrms_ampdu > 64)
+               return -EINVAL;
+
+       if (max_subfrms_amsdu == 0 || max_subfrms_amsdu > 31)
+               return -EINVAL;
+
+       len = sizeof(cmd->hdr);
+       len += sizeof(cmd->aggr_conf);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_AGGR_CFG;
+
+       aggr_conf = &cmd->aggr_conf;
+       aggr_conf->max_num_ampdu_subframes = max_subfrms_ampdu;
+       aggr_conf->max_num_amsdu_subframes = max_subfrms_amsdu;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d",
+                  aggr_conf->max_num_amsdu_subframes,
+                  aggr_conf->max_num_ampdu_subframes);
+
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 {
        struct device *dev = htt->ar->dev;
index d0004d59c97ec6c9292266662d9fd93cdd9eaf5e..06840d101c45cb74c9fd0655fa943b623cf1f7c6 100644 (file)
@@ -1362,8 +1362,6 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
                ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
        }
 
-       init_completion(&xfer.done);
-
        ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
        if (ret)
                goto err_resp;
@@ -1414,10 +1412,7 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
                                          &nbytes, &transfer_id))
                return;
 
-       if (xfer->wait_for_resp)
-               return;
-
-       complete(&xfer->done);
+       xfer->tx_done = true;
 }
 
 static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
@@ -1438,7 +1433,7 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
        }
 
        xfer->resp_len = nbytes;
-       complete(&xfer->done);
+       xfer->rx_done = true;
 }
 
 static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
@@ -1451,7 +1446,7 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
                ath10k_pci_bmi_send_done(tx_pipe);
                ath10k_pci_bmi_recv_data(rx_pipe);
 
-               if (completion_done(&xfer->done))
+               if (xfer->tx_done && (xfer->rx_done == xfer->wait_for_resp))
                        return 0;
 
                schedule();
index dfdebb4157aa177acde13ea1149b5a7490c5593e..940129209990337a1a99f79599014abc48b6b605 100644 (file)
@@ -38,7 +38,8 @@
 #define DIAG_TRANSFER_LIMIT 2048
 
 struct bmi_xfer {
-       struct completion done;
+       bool tx_done;
+       bool rx_done;
        bool wait_for_resp;
        u32 resp_len;
 };
index 4b7782a529ac6b03c6a1bcd764dabc6568b70a6b..6f83cae5765513eeb6b3efb803a657faa6bc5703 100644 (file)
@@ -2106,7 +2106,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_event_id id;
-       u16 len;
 
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
@@ -2114,8 +2113,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
        if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
                return;
 
-       len = skb->len;
-
        trace_ath10k_wmi_event(id, skb->data, skb->len);
 
        switch (id) {
@@ -2225,7 +2222,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_10x_event_id id;
-       u16 len;
 
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
@@ -2233,8 +2229,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
        if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
                return;
 
-       len = skb->len;
-
        trace_ath10k_wmi_event(id, skb->data, skb->len);
 
        switch (id) {
index 18fdd69e1f718dfd8eae5551d8fad9131613651f..397a52f2628b74e1f42a63c9a67d93f9c2407ffc 100644 (file)
@@ -242,7 +242,8 @@ struct ath6kl_bmi_target_info {
                (void) (check_type == val);                             \
                addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item));      \
                ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4);        \
-               *val = le32_to_cpu(tmp);                                \
+               if (!ret)                                               \
+                       *val = le32_to_cpu(tmp);                        \
                ret;                                                    \
        })
 
index 0e26f4a34fda329910ecc278de9fe7bea8fa6c57..d139f2a7160a28120d423483070616529630e950 100644 (file)
@@ -2899,7 +2899,8 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        if (info->inactivity_timeout) {
                inactivity_timeout = info->inactivity_timeout;
 
-               if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
+               if (test_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+                            ar->fw_capabilities))
                        inactivity_timeout = DIV_ROUND_UP(inactivity_timeout,
                                                          60);
 
@@ -3782,7 +3783,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
                ath6kl_band_5ghz.ht_cap.ht_supported = false;
        }
 
-       if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) {
+       if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                    ar->fw_capabilities)) {
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
index b0b6520427600a05687e299318f78f873e3fd948..0df74b245af4c0180ea9219c8765453241aea9f0 100644 (file)
@@ -123,6 +123,22 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
 
        /* FIXME: we should free all firmwares in the error cases below */
 
+       /*
+        * Backwards compatibility support for older ar6004 firmware images
+        * which do not set these feature flags.
+        */
+       if (ar->target_type == TARGET_TYPE_AR6004 &&
+           ar->fw_api <= 4) {
+               __set_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                         ar->fw_capabilities);
+               __set_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+                         ar->fw_capabilities);
+
+               if (ar->hw.id == AR6004_HW_1_3_VERSION)
+                       __set_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                                 ar->fw_capabilities);
+       }
+
        /* Indicate that WMI is enabled (although not ready yet) */
        set_bit(WMI_ENABLED, &ar->flag);
        ar->wmi = ath6kl_wmi_init(ar);
index 26b0f92424e16fb91beca55f3f07e9d907a9db04..2b78c863d03095b035991d3d123ea375b35a4cb6 100644 (file)
@@ -136,6 +136,21 @@ enum ath6kl_fw_capability {
         */
        ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
 
+       /* WMI_SET_TX_SELECT_RATES_CMDID uses 64 bit size rate table */
+       ATH6KL_FW_CAPABILITY_64BIT_RATES,
+
+       /* WMI_AP_CONN_INACT_CMDID uses minutes as units */
+       ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+
+       /* use low priority endpoint for all data */
+       ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+
+       /* ratetable is the 2 stream version (max MCS15) */
+       ATH6KL_FW_CAPABILITY_RATETABLE_MCS15,
+
+       /* firmare doesn't support IP checksumming */
+       ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM,
+
        /* this needs to be last */
        ATH6KL_FW_CAPABILITY_MAX,
 };
@@ -149,15 +164,13 @@ struct ath6kl_fw_ie {
 };
 
 enum ath6kl_hw_flags {
-       ATH6KL_HW_64BIT_RATES           = BIT(0),
-       ATH6KL_HW_AP_INACTIVITY_MINS    = BIT(1),
-       ATH6KL_HW_MAP_LP_ENDPOINT       = BIT(2),
        ATH6KL_HW_SDIO_CRC_ERROR_WAR    = BIT(3),
 };
 
 #define ATH6KL_FW_API2_FILE "fw-2.bin"
 #define ATH6KL_FW_API3_FILE "fw-3.bin"
 #define ATH6KL_FW_API4_FILE "fw-4.bin"
+#define ATH6KL_FW_API5_FILE "fw-5.bin"
 
 /* AR6003 1.0 definitions */
 #define AR6003_HW_1_0_VERSION                 0x300002ba
@@ -215,8 +228,21 @@ enum ath6kl_hw_flags {
 #define AR6004_HW_1_3_VERSION                  0x31c8088a
 #define AR6004_HW_1_3_FW_DIR                   "ath6k/AR6004/hw1.3"
 #define AR6004_HW_1_3_FIRMWARE_FILE            "fw.ram.bin"
-#define AR6004_HW_1_3_BOARD_DATA_FILE          "ath6k/AR6004/hw1.3/bdata.bin"
-#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE  "ath6k/AR6004/hw1.3/bdata.bin"
+#define AR6004_HW_1_3_TCMD_FIRMWARE_FILE       "utf.bin"
+#define AR6004_HW_1_3_UTF_FIRMWARE_FILE                "utf.bin"
+#define AR6004_HW_1_3_TESTSCRIPT_FILE          "nullTestFlow.bin"
+#define AR6004_HW_1_3_BOARD_DATA_FILE        AR6004_HW_1_3_FW_DIR "/bdata.bin"
+#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE AR6004_HW_1_3_FW_DIR "/bdata.bin"
+
+/* AR6004 3.0 definitions */
+#define AR6004_HW_3_0_VERSION                  0x31C809F8
+#define AR6004_HW_3_0_FW_DIR                   "ath6k/AR6004/hw3.0"
+#define AR6004_HW_3_0_FIRMWARE_FILE            "fw.ram.bin"
+#define AR6004_HW_3_0_TCMD_FIRMWARE_FILE       "utf.bin"
+#define AR6004_HW_3_0_UTF_FIRMWARE_FILE                "utf.bin"
+#define AR6004_HW_3_0_TESTSCRIPT_FILE          "nullTestFlow.bin"
+#define AR6004_HW_3_0_BOARD_DATA_FILE        AR6004_HW_3_0_FW_DIR "/bdata.bin"
+#define AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE AR6004_HW_3_0_FW_DIR "/bdata.bin"
 
 /* Per STA data, used in AP mode */
 #define STA_PS_AWAKE           BIT(0)
index 756fe52a12c8ad5a3496de58bbfec01d0ad9820b..ca1a18c86c0d79d64ad93f26f42fdaf87bcb6411 100644 (file)
@@ -1170,8 +1170,12 @@ static int htc_wait_recv_ctrl_message(struct htc_target *target)
 static void htc_rxctrl_complete(struct htc_target *context,
                                struct htc_packet *packet)
 {
-       /* TODO, can't really receive HTC control messages yet.... */
-       ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__);
+       struct sk_buff *skb = packet->skb;
+
+       if (packet->endpoint == ENDPOINT_0 &&
+           packet->status == -ECANCELED &&
+           skb != NULL)
+               dev_kfree_skb(skb);
 }
 
 /* htc pipe initialization */
@@ -1678,7 +1682,29 @@ static void ath6kl_htc_pipe_activity_changed(struct htc_target *target,
 
 static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target)
 {
-       /* TODO */
+       struct htc_endpoint *endpoint;
+       struct htc_packet *packet, *tmp_pkt;
+       int i;
+
+       for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+               endpoint = &target->endpoint[i];
+
+               spin_lock_bh(&target->rx_lock);
+
+               list_for_each_entry_safe(packet, tmp_pkt,
+                                        &endpoint->rx_bufq, list) {
+                       list_del(&packet->list);
+                       spin_unlock_bh(&target->rx_lock);
+                       ath6kl_dbg(ATH6KL_DBG_HTC,
+                                  "htc rx flush pkt 0x%p len %d ep %d\n",
+                                  packet, packet->buf_len,
+                                  packet->endpoint);
+                       dev_kfree_skb(packet->pkt_cntxt);
+                       spin_lock_bh(&target->rx_lock);
+               }
+
+               spin_unlock_bh(&target->rx_lock);
+       }
 }
 
 static int ath6kl_htc_pipe_credit_setup(struct htc_target *target,
index d5ef211f261c2c19e6e8deeef985dc2b83130794..a61118484de6171b04ad004a0bb3a1be73d12c29 100644 (file)
@@ -93,8 +93,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x433900,
                .refclk_hz                      = 26000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_0_FW_DIR,
@@ -114,8 +113,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x43d400,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
                .fw = {
                        .dir            = AR6004_HW_1_1_FW_DIR,
                        .fw             = AR6004_HW_1_1_FIRMWARE_FILE,
@@ -134,8 +132,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x435c00,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_2_FW_DIR,
@@ -152,20 +149,43 @@ static const struct ath6kl_hw hw_list[] = {
                .board_ext_data_addr            = 0x437000,
                .reserved_ram_size              = 7168,
                .board_addr                     = 0x436400,
-               .refclk_hz                      = 40000000,
+               .refclk_hz                      = 0,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS |
-                                                 ATH6KL_HW_MAP_LP_ENDPOINT,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_3_FW_DIR,
                        .fw             = AR6004_HW_1_3_FIRMWARE_FILE,
+                       .tcmd           = AR6004_HW_1_3_TCMD_FIRMWARE_FILE,
+                       .utf            = AR6004_HW_1_3_UTF_FIRMWARE_FILE,
+                       .testscript     = AR6004_HW_1_3_TESTSCRIPT_FILE,
                },
 
                .fw_board               = AR6004_HW_1_3_BOARD_DATA_FILE,
                .fw_default_board       = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE,
        },
+       {
+               .id                             = AR6004_HW_3_0_VERSION,
+               .name                           = "ar6004 hw 3.0",
+               .dataset_patch_addr             = 0,
+               .app_load_addr                  = 0x1234,
+               .board_ext_data_addr            = 0,
+               .reserved_ram_size              = 7168,
+               .board_addr                     = 0x436400,
+               .testscript_addr                = 0,
+               .flags                          = 0,
+
+               .fw = {
+                       .dir            = AR6004_HW_3_0_FW_DIR,
+                       .fw             = AR6004_HW_3_0_FIRMWARE_FILE,
+                       .tcmd           = AR6004_HW_3_0_TCMD_FIRMWARE_FILE,
+                       .utf            = AR6004_HW_3_0_UTF_FIRMWARE_FILE,
+                       .testscript     = AR6004_HW_3_0_TESTSCRIPT_FILE,
+               },
+
+               .fw_board               = AR6004_HW_3_0_BOARD_DATA_FILE,
+               .fw_default_board       = AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE,
+       },
 };
 
 /*
@@ -601,7 +621,9 @@ int ath6kl_configure_target(struct ath6kl *ar)
         * but possible in theory.
         */
 
-       if (ar->target_type == TARGET_TYPE_AR6003) {
+       if ((ar->target_type == TARGET_TYPE_AR6003) ||
+           (ar->version.target_ver == AR6004_HW_1_3_VERSION) ||
+           (ar->version.target_ver == AR6004_HW_3_0_VERSION)) {
                param = ar->hw.board_ext_data_addr;
                ram_reserved_size = ar->hw.reserved_ram_size;
 
@@ -629,9 +651,12 @@ int ath6kl_configure_target(struct ath6kl *ar)
                return status;
 
        /* Configure target refclk_hz */
-       status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz);
-       if (status)
-               return status;
+       if (ar->hw.refclk_hz != 0) {
+               status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz,
+                                              ar->hw.refclk_hz);
+               if (status)
+                       return status;
+       }
 
        return 0;
 }
@@ -1112,6 +1137,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar)
        if (ret)
                return ret;
 
+       ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API5_FILE);
+       if (ret == 0) {
+               ar->fw_api = 5;
+               goto out;
+       }
+
        ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE);
        if (ret == 0) {
                ar->fw_api = 4;
@@ -1161,11 +1192,19 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
                ath6kl_bmi_write_hi32(ar, hi_board_data,
                                      board_address);
        } else {
-               ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address);
+               ret = ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address);
+               if (ret) {
+                       ath6kl_err("Failed to get board file target address.\n");
+                       return ret;
+               }
        }
 
        /* determine where in target ram to write extended board data */
-       ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address);
+       ret = ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address);
+       if (ret) {
+               ath6kl_err("Failed to get extended board file target address.\n");
+               return ret;
+       }
 
        if (ar->target_type == TARGET_TYPE_AR6003 &&
            board_ext_address == 0) {
@@ -1230,7 +1269,13 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
        }
 
        /* record the fact that Board Data IS initialized */
-       ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1);
+       if ((ar->version.target_ver == AR6004_HW_1_3_VERSION) ||
+           (ar->version.target_ver == AR6004_HW_3_0_VERSION))
+               param = board_data_size;
+       else
+               param = 1;
+
+       ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, param);
 
        return ret;
 }
@@ -1361,7 +1406,11 @@ static int ath6kl_upload_testscript(struct ath6kl *ar)
        }
 
        ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address);
-       ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096);
+
+       if ((ar->version.target_ver != AR6004_HW_1_3_VERSION) &&
+           (ar->version.target_ver != AR6004_HW_3_0_VERSION))
+               ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096);
+
        ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1);
 
        return 0;
@@ -1567,6 +1616,11 @@ static const struct fw_capa_str_map {
        { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" },
        { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" },
        { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" },
+       { ATH6KL_FW_CAPABILITY_64BIT_RATES, "64bit-rates" },
+       { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" },
+       { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" },
+       { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" },
+       { ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, "no-ip-checksum" },
 };
 
 static const char *ath6kl_init_get_fw_capa_name(unsigned int id)
index d56554674da47924477c02423ad08c50caef2918..21516bc657857b8a44405c5c22944a275271e5f2 100644 (file)
@@ -702,6 +702,7 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
        struct ath6kl *ar = vif->ar;
        struct target_stats *stats = &vif->target_stats;
        struct tkip_ccmp_stats *ccmp_stats;
+       s32 rate;
        u8 ac;
 
        if (len < sizeof(*tgt_stats))
@@ -731,8 +732,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
                le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt);
        stats->tx_rts_fail_cnt +=
                le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt);
-       stats->tx_ucast_rate =
-           ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate));
+
+       rate = a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate);
+       stats->tx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
 
        stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt);
        stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte);
@@ -749,8 +751,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
                le32_to_cpu(tgt_stats->stats.rx.key_cache_miss);
        stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err);
        stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame);
-       stats->rx_ucast_rate =
-           ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate));
+
+       rate = a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate);
+       stats->rx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
 
        ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats;
 
@@ -1290,6 +1293,8 @@ static const struct net_device_ops ath6kl_netdev_ops = {
 
 void init_netdev(struct net_device *dev)
 {
+       struct ath6kl *ar = ath6kl_priv(dev);
+
        dev->netdev_ops = &ath6kl_netdev_ops;
        dev->destructor = free_netdev;
        dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
@@ -1301,7 +1306,9 @@ void init_netdev(struct net_device *dev)
                                        WMI_MAX_TX_META_SZ +
                                        ATH6KL_HTC_ALIGN_BYTES, 4);
 
-       dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+       if (!test_bit(ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM,
+                     ar->fw_capabilities))
+               dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
 
        return;
 }
index 3afc5a463d06f822f339250deecfb1cefadea592..c44325856b81fc4cee191ec9aceb2102f2f0b23b 100644 (file)
@@ -802,7 +802,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id,
                break;
        case WMI_DATA_VI_SVC:
 
-               if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+               if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                            ar->fw_capabilities))
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
                else
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
@@ -814,7 +815,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id,
                break;
        case WMI_DATA_VO_SVC:
 
-               if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+               if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                            ar->fw_capabilities))
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
                else
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
@@ -1208,6 +1210,7 @@ static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
 
 /* table of devices that work with this driver */
 static struct usb_device_id ath6kl_usb_ids[] = {
+       {USB_DEVICE(0x0cf3, 0x9375)},
        {USB_DEVICE(0x0cf3, 0x9374)},
        { /* Terminating entry */ },
 };
index 4d7f9e4712e991deea8553f7505a7f79b9a6f6e7..94df345d08c24210fefe4b707079f85fe75e386f 100644 (file)
@@ -59,6 +59,55 @@ static const s32 wmi_rate_tbl[][2] = {
        {0, 0}
 };
 
+static const s32 wmi_rate_tbl_mcs15[][2] = {
+       /* {W/O SGI, with SGI} */
+       {1000, 1000},
+       {2000, 2000},
+       {5500, 5500},
+       {11000, 11000},
+       {6000, 6000},
+       {9000, 9000},
+       {12000, 12000},
+       {18000, 18000},
+       {24000, 24000},
+       {36000, 36000},
+       {48000, 48000},
+       {54000, 54000},
+       {6500, 7200},     /* HT 20, MCS 0 */
+       {13000, 14400},
+       {19500, 21700},
+       {26000, 28900},
+       {39000, 43300},
+       {52000, 57800},
+       {58500, 65000},
+       {65000, 72200},
+       {13000, 14400},   /* HT 20, MCS 8 */
+       {26000, 28900},
+       {39000, 43300},
+       {52000, 57800},
+       {78000, 86700},
+       {104000, 115600},
+       {117000, 130000},
+       {130000, 144400}, /* HT 20, MCS 15 */
+       {13500, 15000},   /*HT 40, MCS 0 */
+       {27000, 30000},
+       {40500, 45000},
+       {54000, 60000},
+       {81000, 90000},
+       {108000, 120000},
+       {121500, 135000},
+       {135000, 150000},
+       {27000, 30000},   /*HT 40, MCS 8 */
+       {54000, 60000},
+       {81000, 90000},
+       {108000, 120000},
+       {162000, 180000},
+       {216000, 240000},
+       {243000, 270000},
+       {270000, 300000}, /*HT 40, MCS 15 */
+       {0, 0}
+};
+
 /* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
 static const u8 up_to_ac[] = {
        WMM_AC_BE,
@@ -2838,7 +2887,8 @@ int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
 {
        struct ath6kl *ar = wmi->parent_dev;
 
-       if (ar->hw.flags & ATH6KL_HW_64BIT_RATES)
+       if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                    ar->fw_capabilities))
                return ath6kl_set_bitrate_mask64(wmi, if_idx, mask);
        else
                return ath6kl_set_bitrate_mask32(wmi, if_idx, mask);
@@ -3279,9 +3329,11 @@ int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2)
                                   NO_SYNC_WMIFLAG);
 }
 
-s32 ath6kl_wmi_get_rate(s8 rate_index)
+s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index)
 {
+       struct ath6kl *ar = wmi->parent_dev;
        u8 sgi = 0;
+       s32 ret;
 
        if (rate_index == RATE_AUTO)
                return 0;
@@ -3292,10 +3344,20 @@ s32 ath6kl_wmi_get_rate(s8 rate_index)
                sgi = 1;
        }
 
-       if (WARN_ON(rate_index > RATE_MCS_7_40))
-               rate_index = RATE_MCS_7_40;
+       if (test_bit(ATH6KL_FW_CAPABILITY_RATETABLE_MCS15,
+                    ar->fw_capabilities)) {
+               if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl_mcs15)))
+                       return 0;
+
+               ret = wmi_rate_tbl_mcs15[(u32) rate_index][sgi];
+       } else {
+               if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl)))
+                       return 0;
 
-       return wmi_rate_tbl[(u32) rate_index][sgi];
+               ret = wmi_rate_tbl[(u32) rate_index][sgi];
+       }
+
+       return ret;
 }
 
 static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
index bb23fc00111dc6c3329762ee976ce93489015b90..19f88b4a24fbcef48bb31888abcd92a5bb0ce354 100644 (file)
@@ -2632,7 +2632,7 @@ int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
                             struct ath6kl_htcap *htcap);
 int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
 
-s32 ath6kl_wmi_get_rate(s8 rate_index);
+s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index);
 
 int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx,
                          __be32 ips0, __be32 ips1);
index 741b38ddcb378e8cb17dbfe0f79f33516be75527..59af9f9712da852e0ebf4e05747b35da7373dbeb 100644 (file)
@@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower)
+               | SM(i->txpower, AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -306,6 +306,10 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
                | set11nRateFlags(i->rates, 2)
                | set11nRateFlags(i->rates, 3)
                | SM(i->rtscts_rate, AR_RTSCTSRate);
+
+       ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
+       ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
+       ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
 }
 
 static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
index 729ffbf07343bb72c9313f037cc5965e58d8366f..71e38e85aa99801546e8e64332bbce3458edd604 100644 (file)
@@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower)
+               | SM(i->txpower, AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
                | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -151,6 +151,10 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
                | SM(i->rtscts_rate, AR_RTSCTSRate);
 
        ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+
+       ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
+       ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
+       ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
 }
 
 static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
index 11b5e4dd629491179809aa89ef23aa9e57f5c6c4..7fc13a8da6757347e4daff1d1ede61da93648d3d 100644 (file)
@@ -182,7 +182,8 @@ struct ath_atx_ac {
 
 struct ath_frame_info {
        struct ath_buf *bf;
-       int framelen;
+       u16 framelen;
+       s8 txq;
        enum ath9k_key_type keytype;
        u8 keyix;
        u8 rtscts_rate;
index ce073e995dfe3c50bfe128e5f3eadb1d5c9f5663..d2279365be6fcc6a85b2252a9c68a2502f94c211 100644 (file)
@@ -202,7 +202,7 @@ static ssize_t write_file_ani(struct file *file,
        if (kstrtoul(buf, 0, &ani))
                return -EINVAL;
 
-       if (ani < 0 || ani > 1)
+       if (ani > 1)
                return -EINVAL;
 
        common->disable_ani = !ani;
index da768675753595c3c27ef3fd920327af3c1a547e..6c56cafa5ca491fb284adad4fb156af9caa322b4 100644 (file)
@@ -346,8 +346,14 @@ struct ar5416_desc {
 #define AR_FrameLen         0x00000fff
 #define AR_VirtMoreFrag     0x00001000
 #define AR_TxCtlRsvd00      0x0000e000
-#define AR_XmitPower        0x003f0000
-#define AR_XmitPower_S      16
+#define AR_XmitPower0       0x003f0000
+#define AR_XmitPower0_S     16
+#define AR_XmitPower1      0x3f000000
+#define AR_XmitPower1_S     24
+#define AR_XmitPower2      0x3f000000
+#define AR_XmitPower2_S     24
+#define AR_XmitPower3      0x3f000000
+#define AR_XmitPower3_S     24
 #define AR_RTSEnable        0x00400000
 #define AR_VEOL             0x00800000
 #define AR_ClrDestMask      0x01000000
index 99f4de95c264b0638108d58820efcb9b8390e8fc..5fe29b9f8fa26e31b05eaab237ac4830aaf21471 100644 (file)
@@ -313,7 +313,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 1)
+       if (val > 1)
                return -EINVAL;
 
        sc->spec_config.short_repeat = val;
@@ -361,7 +361,7 @@ static ssize_t write_file_spectral_count(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 255)
+       if (val > 255)
                return -EINVAL;
 
        sc->spec_config.count = val;
@@ -409,7 +409,7 @@ static ssize_t write_file_spectral_period(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 255)
+       if (val > 255)
                return -EINVAL;
 
        sc->spec_config.period = val;
@@ -457,7 +457,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 15)
+       if (val > 15)
                return -EINVAL;
 
        sc->spec_config.fft_period = val;
index d4927c9a6bae9a7cf86315bfbd7e8a1374260ca3..36115298f64e95678a767a1098bea1bb0d042b57 100644 (file)
@@ -157,15 +157,14 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
                             struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int q, hw_queue;
-
-       q = skb_get_queue_mapping(skb);
-       if (txq == sc->tx.uapsdq)
-               txq = sc->tx.txq_map[q];
+       struct ath_frame_info *fi = get_frame_info(skb);
+       int hw_queue;
+       int q = fi->txq;
 
-       if (txq != sc->tx.txq_map[q])
+       if (q < 0)
                return;
 
+       txq = sc->tx.txq_map[q];
        if (WARN_ON(--txq->pending_frames < 0))
                txq->pending_frames = 0;
 
@@ -2036,6 +2035,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
                an = (struct ath_node *) sta->drv_priv;
 
        memset(fi, 0, sizeof(*fi));
+       fi->txq = -1;
        if (hw_key)
                fi->keyix = hw_key->hw_key_idx;
        else if (an && ieee80211_is_data(hdr->frame_control) && an->ps_key > 0)
@@ -2187,6 +2187,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
+       struct ath_frame_info *fi = get_frame_info(skb);
        struct ath_vif *avp = NULL;
        struct ath_softc *sc = hw->priv;
        struct ath_txq *txq = txctl->txq;
@@ -2216,11 +2217,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
 
        ath_txq_lock(sc, txq);
-       if (txq == sc->tx.txq_map[q] &&
-           ++txq->pending_frames > sc->tx.txq_max_pending[q] &&
-           !txq->stopped) {
-               ieee80211_stop_queue(sc->hw, hw_queue);
-               txq->stopped = true;
+       if (txq == sc->tx.txq_map[q]) {
+               fi->txq = q;
+               if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
+                   !txq->stopped) {
+                       ieee80211_stop_queue(sc->hw, hw_queue);
+                       txq->stopped = true;
+               }
        }
 
        queue = ieee80211_is_data_present(hdr->frame_control);
index a868c5eebe37d73a0c4e140473fb2f3e02169278..8f66186adb8c59d67103ad75ddb759063736bbaa 100644 (file)
@@ -448,8 +448,10 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
        char *kbuf = kmalloc(len + 1, GFP_KERNEL);
        if (!kbuf)
                return -ENOMEM;
-       if (copy_from_user(kbuf, buf, len))
+       if (copy_from_user(kbuf, buf, len)) {
+               kfree(kbuf);
                return -EIO;
+       }
 
        kbuf[len] = '\0';
        rc = kstrtol(kbuf, 0, &channel);
@@ -963,6 +965,26 @@ static const struct file_operations fops_sta = {
 };
 
 /*----------------*/
+static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
+                                      struct dentry *dbg)
+{
+       int i;
+       char name[32];
+
+       for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
+               struct debugfs_blob_wrapper *blob = &wil->blobs[i];
+               const struct fw_map *map = &fw_mapping[i];
+
+               if (!map->name)
+                       continue;
+
+               blob->data = (void * __force)wil->csr + HOSTADDR(map->host);
+               blob->size = map->to - map->from;
+               snprintf(name, sizeof(name), "blob_%s", map->name);
+               wil_debugfs_create_ioblob(name, S_IRUGO, dbg, blob);
+       }
+}
+
 int wil6210_debugfs_init(struct wil6210_priv *wil)
 {
        struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
@@ -986,6 +1008,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
                           &wil->secure_pcp);
        wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
                                 &wil->status);
+       debugfs_create_u32("fw_version", S_IRUGO, dbg, &wil->fw_version);
+       debugfs_create_x32("hw_version", S_IRUGO, dbg, &wil->hw_version);
 
        wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
                                   HOSTADDR(RGF_USER_USER_ICR));
@@ -998,6 +1022,9 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        wil6210_debugfs_create_pseudo_ISR(wil, dbg);
        wil6210_debugfs_create_ITR_CNT(wil, dbg);
 
+       wil_debugfs_create_iomem_x32("RGF_USER_USAGE_1", S_IRUGO, dbg,
+                                    wil->csr +
+                                    HOSTADDR(RGF_USER_USAGE_1));
        debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr);
        debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
 
@@ -1010,34 +1037,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
        debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
 
-       wil->rgf_blob.data = (void * __force)wil->csr + 0;
-       wil->rgf_blob.size = 0xa000;
-       wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob);
-
-       wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000;
-       wil->fw_code_blob.size = 0x40000;
-       wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg,
-                                 &wil->fw_code_blob);
-
-       wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000;
-       wil->fw_data_blob.size = 0x8000;
-       wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg,
-                                 &wil->fw_data_blob);
-
-       wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000;
-       wil->fw_peri_blob.size = 0x18000;
-       wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg,
-                                 &wil->fw_peri_blob);
-
-       wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000;
-       wil->uc_code_blob.size = 0x10000;
-       wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg,
-                                 &wil->uc_code_blob);
-
-       wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000;
-       wil->uc_data_blob.size = 0x4000;
-       wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg,
-                                 &wil->uc_data_blob);
+       wil6210_debugfs_init_blobs(wil, dbg);
 
        return 0;
 }
index 53a689ed7c7d0ba053d652c4b696639639b617d7..3704d2a434f340b4374f26e5bde083b9830e09a3 100644 (file)
@@ -314,8 +314,9 @@ static void wil_target_reset(struct wil6210_priv *wil)
        int delay = 0;
        u32 hw_state;
        u32 rev_id;
+       bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW);
 
-       wil_dbg_misc(wil, "Resetting...\n");
+       wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name);
 
        /* register read */
 #define R(a) ioread32(wil->csr + HOSTADDR(a))
@@ -328,35 +329,59 @@ static void wil_target_reset(struct wil6210_priv *wil)
 
        wil->hw_version = R(RGF_USER_FW_REV_ID);
        rev_id = wil->hw_version & 0xff;
+
+       /* Clear MAC link up */
+       S(RGF_HP_CTRL, BIT(15));
        /* hpal_perst_from_pad_src_n_mask */
        S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
        /* car_perst_rst_src_n_mask */
        S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
        wmb(); /* order is important here */
 
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
+               wmb(); /* order is important here */
+       }
+
        W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */
        W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
        wmb(); /* order is important here */
 
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
-       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000B0 : 0x00000170);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
        wmb(); /* order is important here */
 
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
+               wmb(); /* order is important here */
+       }
+
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
        wmb(); /* order is important here */
 
-       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
-       if (rev_id == 1) {
-               W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
-       } else {
-               W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
+               /* reset A2 PCIE AHB */
                W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+
+       } else {
+               W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
+               if (rev_id == 1) {
+                       /* reset A1 BOTH PCIE AHB & PCIE RGF */
+                       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+               } else {
+                       W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+                       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+               }
+
        }
+
+       /* TODO: check order here!!! Erez code is different */
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
        wmb(); /* order is important here */
 
@@ -371,7 +396,8 @@ static void wil_target_reset(struct wil6210_priv *wil)
                }
        } while (hw_state != HW_MACHINE_BOOT_DONE);
 
-       if (rev_id == 2)
+       /* TODO: Erez check rev_id != 1 */
+       if (!is_sparrow && (rev_id != 1))
                W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
 
        C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
index 77b6272d93fb24c11e9d703549e03929f623c6ca..d3fbfa28db62c8af2c95f824f9801b516635abff 100644 (file)
@@ -122,10 +122,12 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        struct wil6210_priv *wil;
        struct device *dev = &pdev->dev;
        void __iomem *csr;
+       struct wil_board *board = (struct wil_board *)id->driver_data;
        int rc;
 
        /* check HW */
-       dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n",
+       dev_info(&pdev->dev, WIL_NAME
+                " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name,
                 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
 
        if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
@@ -175,6 +177,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        pci_set_drvdata(pdev, wil);
        wil->pdev = pdev;
+       wil->board = board;
 
        wil6210_clear_irq(wil);
        /* FW should raise IRQ when ready */
@@ -225,8 +228,21 @@ static void wil_pcie_remove(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
-       { PCI_DEVICE(0x1ae9, 0x0301) },
+static const struct wil_board wil_board_marlon = {
+       .board = WIL_BOARD_MARLON,
+       .name = "marlon",
+};
+
+static const struct wil_board wil_board_sparrow = {
+       .board = WIL_BOARD_SPARROW,
+       .name = "sparrow",
+};
+
+static const struct pci_device_id wil6210_pcie_ids[] = {
+       { PCI_DEVICE(0x1ae9, 0x0301),
+         .driver_data = (kernel_ulong_t)&wil_board_marlon },
+       { PCI_DEVICE(0x1ae9, 0x0310),
+         .driver_data = (kernel_ulong_t)&wil_board_sparrow },
        { /* end: all zeroes */ },
 };
 MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
index af4b93e4beb5e80e9412785d60a17ebbe95e790a..d3467943d39db60972bae0ce0413b4e5eaa04585 100644 (file)
@@ -1108,8 +1108,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                while (vring->swtail != new_swtail) {
                        struct vring_tx_desc dd, *d = &dd;
                        u16 dmalen;
-                       struct wil_ctx *ctx = &vring->ctx[vring->swtail];
-                       struct sk_buff *skb = ctx->skb;
+                       struct sk_buff *skb;
+
+                       ctx = &vring->ctx[vring->swtail];
+                       skb = ctx->skb;
                        _d = &vring->va[vring->swtail].tx;
 
                        *d = *_d;
index 424906635f05dc503b5718c8e8b0f3863e500ae0..67e9624f7111b8842492d81d07665c5ec0973705 100644 (file)
 
 #define WIL_NAME "wil6210"
 
+struct wil_board {
+       int board;
+#define WIL_BOARD_MARLON       (1)
+#define WIL_BOARD_SPARROW      (2)
+       const char * const name;
+};
+
 /**
  * extract bits [@b0:@b1] (inclusive) from the value @x
  * it should be @b0 <= @b1, or result is incorrect
@@ -78,6 +85,7 @@ struct RGF_ICR {
 } __packed;
 
 /* registers - FW addresses */
+#define RGF_USER_USAGE_1               (0x880004)
 #define RGF_USER_HW_MACHINE_STATE      (0x8801dc)
        #define HW_MACHINE_BOOT_DONE    (0x3fffffd)
 #define RGF_USER_USER_CPU_0            (0x8801e0)
@@ -93,6 +101,7 @@ struct RGF_ICR {
 #define RGF_USER_CLKS_CTL_SW_RST_MASK_0        (0x880b14)
 #define RGF_USER_USER_ICR              (0x880b4c) /* struct RGF_ICR */
        #define BIT_USER_USER_ICR_SW_INT_2      BIT(18)
+#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0     (0x880c18)
 
 #define RGF_DMA_EP_TX_ICR              (0x881bb4) /* struct RGF_ICR */
        #define BIT_DMA_EP_TX_ICR_TX_DONE       BIT(0)
@@ -121,6 +130,7 @@ struct RGF_ICR {
        #define BIT_DMA_PSEUDO_CAUSE_TX         BIT(1)
        #define BIT_DMA_PSEUDO_CAUSE_MISC       BIT(2)
 
+#define RGF_HP_CTRL                    (0x88265c)
 #define RGF_PCIE_LOS_COUNTER_CTL       (0x882dc4)
 
 /* popular locations */
@@ -135,6 +145,14 @@ struct RGF_ICR {
 #define ISR_MISC_FW_ERROR      BIT_DMA_EP_MISC_ICR_FW_INT(3)
 
 /* Hardware definitions end */
+struct fw_map {
+       u32 from; /* linker address - from, inclusive */
+       u32 to;   /* linker address - to, exclusive */
+       u32 host; /* PCI/Host address - BAR0 + 0x880000 */
+       const char *name; /* for debugfs */
+};
+/* array size should be in sync with actual definition in the wmi.c */
+extern const struct fw_map fw_mapping[7];
 
 /**
  * mk_cidxtid - construct @cidxtid field
@@ -365,6 +383,7 @@ struct wil6210_priv {
        ulong status;
        u32 fw_version;
        u32 hw_version;
+       struct wil_board *board;
        u8 n_mids; /* number of additional MIDs as reported by FW */
        int recovery_count; /* num of FW recovery attempts in a short time */
        unsigned long last_fw_recovery; /* jiffies of last fw recovery */
@@ -415,12 +434,7 @@ struct wil6210_priv {
        atomic_t isr_count_rx, isr_count_tx;
        /* debugfs */
        struct dentry *debug;
-       struct debugfs_blob_wrapper fw_code_blob;
-       struct debugfs_blob_wrapper fw_data_blob;
-       struct debugfs_blob_wrapper fw_peri_blob;
-       struct debugfs_blob_wrapper uc_code_blob;
-       struct debugfs_blob_wrapper uc_data_blob;
-       struct debugfs_blob_wrapper rgf_blob;
+       struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
 };
 
 #define wil_to_wiphy(i) (i->wdev->wiphy)
index a136dab560e22c94e7c30682d56cbfbb9144745c..1d1d0afdd2e195c6856372299e3fc635b213df23 100644 (file)
 
 /**
  * @fw_mapping provides memory remapping table
+ *
+ * array size should be in sync with the declaration in the wil6210.h
  */
-static const struct {
-       u32 from; /* linker address - from, inclusive */
-       u32 to;   /* linker address - to, exclusive */
-       u32 host; /* PCI/Host address - BAR0 + 0x880000 */
-} fw_mapping[] = {
-       {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */
-       {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
-       {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
-       {0x880000, 0x88a000, 0x880000}, /* various RGF */
-       {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */
-       {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
+const struct fw_map fw_mapping[] = {
+       {0x000000, 0x040000, 0x8c0000, "fw_code"}, /* FW code RAM      256k */
+       {0x800000, 0x808000, 0x900000, "fw_data"}, /* FW data RAM       32k */
+       {0x840000, 0x860000, 0x908000, "fw_peri"}, /* periph. data RAM 128k */
+       {0x880000, 0x88a000, 0x880000, "rgf"},     /* various RGF       40k */
+       {0x88a000, 0x88b000, 0x88a000, "AGC_tbl"}, /* AGC table          4k */
+       {0x88b000, 0x88c000, 0x88b000, "rgf_ext"}, /* Pcie_ext_rgf       4k */
+       {0x8c0000, 0x949000, 0x8c0000, "upper"},   /* upper area       548k */
        /*
         * 920000..930000 ucode code RAM
         * 930000..932000 ucode data RAM
index 4d7b38c07a7f3490d1afd3701725969aa402cc9d..3e127be06bfb988fe2d3a9eada52ccf21f4967fc 100644 (file)
@@ -210,6 +210,9 @@ static struct ieee80211_channel b43_2ghz_chantable[] = {
        CHAN2G(13, 2472, 0),
        CHAN2G(14, 2484, 0),
 };
+
+/* No support for the last 3 channels (12, 13, 14) */
+#define b43_2ghz_chantable_limited_size                11
 #undef CHAN2G
 
 #define CHAN4G(_channel, _flags) {                             \
@@ -335,6 +338,14 @@ static struct ieee80211_supported_band b43_band_2GHz = {
        .n_bitrates     = b43_g_ratetable_size,
 };
 
+static struct ieee80211_supported_band b43_band_2ghz_limited = {
+       .band           = IEEE80211_BAND_2GHZ,
+       .channels       = b43_2ghz_chantable,
+       .n_channels     = b43_2ghz_chantable_limited_size,
+       .bitrates       = b43_g_ratetable,
+       .n_bitrates     = b43_g_ratetable_size,
+};
+
 static void b43_wireless_core_exit(struct b43_wldev *dev);
 static int b43_wireless_core_init(struct b43_wldev *dev);
 static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev);
@@ -2953,6 +2964,45 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on)
        }
 }
 
+/* brcms_b_switch_macfreq */
+void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode)
+{
+       u16 chip_id = dev->dev->chip_id;
+
+       if (chip_id == BCMA_CHIP_ID_BCM43217 ||
+           chip_id == BCMA_CHIP_ID_BCM43222 ||
+           chip_id == BCMA_CHIP_ID_BCM43224 ||
+           chip_id == BCMA_CHIP_ID_BCM43225 ||
+           chip_id == BCMA_CHIP_ID_BCM43227 ||
+           chip_id == BCMA_CHIP_ID_BCM43228) {
+               switch (spurmode) {
+               case 2: /* 126 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               case 1: /* 123 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               default: /* 120 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               }
+       } else if (dev->phy.type == B43_PHYTYPE_LCN) {
+               switch (spurmode) {
+               case 1: /* 82 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+                       break;
+               default: /* 80 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+                       break;
+               }
+       }
+}
+
 static void b43_adjust_opmode(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
@@ -4329,6 +4379,7 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type)
 static int b43_phy_versioning(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       const u8 core_rev = dev->dev->core_rev;
        u32 tmp;
        u8 analog_type;
        u8 phy_type;
@@ -4359,7 +4410,7 @@ static int b43_phy_versioning(struct b43_wldev *dev)
 #endif
 #ifdef CONFIG_B43_PHY_N
        case B43_PHYTYPE_N:
-               if (phy_rev > 9)
+               if (phy_rev >= 19)
                        unsupported = 1;
                break;
 #endif
@@ -4394,7 +4445,15 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev);
 
        /* Get RADIO versioning */
-       if (dev->dev->core_rev >= 24) {
+       if (core_rev == 40 || core_rev == 42) {
+               radio_manuf = 0x17F;
+
+               b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0);
+               radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+
+               b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
+               radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+       } else if (core_rev >= 24) {
                u16 radio24[3];
 
                for (tmp = 0; tmp < 3; tmp++) {
@@ -4450,7 +4509,10 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_N:
-               if (radio_ver != 0x2055 && radio_ver != 0x2056)
+               if (radio_ver != 0x2055 && radio_ver != 0x2056 &&
+                   radio_ver != 0x2057)
+                       unsupported = 1;
+               if (radio_ver == 0x2057 && !(radio_rev == 9))
                        unsupported = 1;
                break;
        case B43_PHYTYPE_LP:
@@ -4469,13 +4531,13 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                B43_WARN_ON(1);
        }
        if (unsupported) {
-               b43err(dev->wl, "FOUND UNSUPPORTED RADIO "
-                      "(Manuf 0x%X, Version 0x%X, Revision %u)\n",
+               b43err(dev->wl,
+                      "FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u)\n",
                       radio_manuf, radio_ver, radio_rev);
                return -EOPNOTSUPP;
        }
-       b43dbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X, Revision %u\n",
-              radio_manuf, radio_ver, radio_rev);
+       b43info(dev->wl, "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u\n",
+               radio_manuf, radio_ver, radio_rev);
 
        phy->radio_manuf = radio_manuf;
        phy->radio_ver = radio_ver;
@@ -5086,9 +5148,15 @@ static int b43_setup_bands(struct b43_wldev *dev,
                           bool have_2ghz_phy, bool have_5ghz_phy)
 {
        struct ieee80211_hw *hw = dev->wl->hw;
+       struct b43_phy *phy = &dev->phy;
+       bool limited_2g;
+
+       /* We don't support all 2 GHz channels on some devices */
+       limited_2g = phy->radio_ver == 0x2057 && phy->radio_rev == 9;
 
        if (have_2ghz_phy)
-               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ?
+                       &b43_band_2ghz_limited : &b43_band_2GHz;
        if (dev->phy.type == B43_PHYTYPE_N) {
                if (have_5ghz_phy)
                        hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy;
@@ -5239,14 +5307,16 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy);
 
        /* We don't support 5 GHz on some PHYs yet */
-       switch (dev->phy.type) {
-       case B43_PHYTYPE_A:
-       case B43_PHYTYPE_G:
-       case B43_PHYTYPE_N:
-       case B43_PHYTYPE_LP:
-       case B43_PHYTYPE_HT:
-               b43warn(wl, "5 GHz band is unsupported on this PHY\n");
-               have_5ghz_phy = false;
+       if (have_5ghz_phy) {
+               switch (dev->phy.type) {
+               case B43_PHYTYPE_A:
+               case B43_PHYTYPE_G:
+               case B43_PHYTYPE_N:
+               case B43_PHYTYPE_LP:
+               case B43_PHYTYPE_HT:
+                       b43warn(wl, "5 GHz band is unsupported on this PHY\n");
+                       have_5ghz_phy = false;
+               }
        }
 
        if (!have_2ghz_phy && !have_5ghz_phy) {
index f476fc337d64c8098ff7397bff7fedd2a9bcc4b0..9f22e4b4c13297207c309d7ea6a1fd884d061655 100644 (file)
@@ -99,6 +99,7 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags);
 void b43_mac_suspend(struct b43_wldev *dev);
 void b43_mac_enable(struct b43_wldev *dev);
 void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);
+void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode);
 
 
 struct b43_request_fw_context;
index 0bafa3b17035db8d5767da2632da343dd1ae70c7..e76bbdf3247e9b095af94bf907b2439f3f25f5f3 100644 (file)
@@ -54,39 +54,6 @@ enum lcn_sense_type {
        B43_SENSE_VBAT,
 };
 
-/* In theory it's PHY common function, move if needed */
-/* brcms_b_switch_macfreq */
-static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode)
-{
-       if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) {
-               switch (spurmode) {
-               case 2:         /* 126 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               case 1:         /* 123 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               default:        /* 120 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               }
-       } else if (dev->phy.type == B43_PHYTYPE_LCN) {
-               switch (spurmode) {
-               case 1:         /* 82 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
-                       break;
-               default:        /* 80 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
-                       break;
-               }
-       }
-}
-
 /**************************************************
  * Radio 2064.
  **************************************************/
@@ -609,7 +576,7 @@ static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev,
                b43_phy_write(dev, 0x93b, ((0 << 13) + 23));
                b43_phy_write(dev, 0x93c, ((0 << 13) + 1989));
        }
-       b43_phy_switch_macfreq(dev, enable);
+       b43_mac_switch_freq(dev, enable);
 }
 
 /**************************************************
index 0a6f04be907364baa53fc4a5513c00577a9609cd..92bfe352ba081c3a83222b9920831268a8f3599f 100644 (file)
@@ -36,6 +36,7 @@
 #include "main.h"
 
 struct nphy_txgains {
+       u16 tx_lpf[2];
        u16 txgm[2];
        u16 pga[2];
        u16 pad[2];
@@ -43,6 +44,7 @@ struct nphy_txgains {
 };
 
 struct nphy_iqcal_params {
+       u16 tx_lpf;
        u16 txgm;
        u16 pga;
        u16 pad;
@@ -69,6 +71,14 @@ enum b43_nphy_rf_sequence {
        B43_RFSEQ_UPDATE_GAINU,
 };
 
+enum n_rf_ctl_over_cmd {
+       N_RF_CTL_OVER_CMD_RXRF_PU = 0,
+       N_RF_CTL_OVER_CMD_RX_PU = 1,
+       N_RF_CTL_OVER_CMD_TX_PU = 2,
+       N_RF_CTL_OVER_CMD_RX_GAIN = 3,
+       N_RF_CTL_OVER_CMD_TX_GAIN = 4,
+};
+
 enum n_intc_override {
        N_INTC_OVERRIDE_OFF = 0,
        N_INTC_OVERRIDE_TRSW = 1,
@@ -140,11 +150,19 @@ ok:
        b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
 }
 
+static void b43_nphy_rf_ctl_override_rev19(struct b43_wldev *dev, u16 field,
+                                          u16 value, u8 core, bool off,
+                                          u8 override_id)
+{
+       /* TODO */
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */
 static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
                                          u16 value, u8 core, bool off,
                                          u8 override)
 {
+       struct b43_phy *phy = &dev->phy;
        const struct nphy_rf_control_override_rev7 *e;
        u16 en_addrs[3][2] = {
                { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 }
@@ -154,6 +172,11 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
        u16 val_addr;
        u8 i;
 
+       if (phy->rev >= 19 || phy->rev < 3) {
+               B43_WARN_ON(1);
+               return;
+       }
+
        /* Remember: we can get NULL! */
        e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override);
 
@@ -181,6 +204,50 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverideOneToMany */
+static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev,
+                                                enum n_rf_ctl_over_cmd cmd,
+                                                u16 value, u8 core, bool off)
+{
+       struct b43_phy *phy = &dev->phy;
+       u16 tmp;
+
+       B43_WARN_ON(phy->rev < 7);
+
+       switch (cmd) {
+       case N_RF_CTL_OVER_CMD_RXRF_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x20, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x10, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x08, value, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_RX_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_TX_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_RX_GAIN:
+               tmp = value & 0xFF;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, tmp, core, off, 0);
+               tmp = value >> 8;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x6000, tmp, core, off, 0);
+               break;
+       case N_RF_CTL_OVER_CMD_TX_GAIN:
+               tmp = value & 0x7FFF;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1000, tmp, core, off, 0);
+               tmp = value >> 14;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4000, tmp, core, off, 0);
+               break;
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */
 static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field,
                                     u16 value, u8 core, bool off)
@@ -264,6 +331,8 @@ static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
        u16 reg, tmp, tmp2, val;
        int core;
 
+       /* TODO: What about rev19+? Revs 3+ and 7+ are a bit similar */
+
        for (core = 0; core < 2; core++) {
                if ((core_sel == 1 && core != 0) ||
                    (core_sel == 2 && core != 1))
@@ -505,6 +574,14 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */
+static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
+{
+       if (!offset)
+               offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
+       return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */
 static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev)
 {
@@ -669,10 +746,63 @@ static void b43_radio_2057_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
                }
                break;
-       /* TODO */
+       case 9: /* e.g. PHY rev 16 */
+               b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x20);
+               b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x18);
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x38);
+                       b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x0f);
+
+                       if (b43_is_40mhz(dev)) {
+                               /* TODO */
+                       } else {
+                               b43_radio_write(dev,
+                                               R2057_PAD_BIAS_FILTER_BWS_CORE0,
+                                               0x3c);
+                               b43_radio_write(dev,
+                                               R2057_PAD_BIAS_FILTER_BWS_CORE1,
+                                               0x3c);
+                       }
+               }
+               break;
+       case 14: /* 2 GHz only */
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1b);
+               b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x1f);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x1f);
+               break;
        }
 
-       /* TODO */
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               u16 txmix2g_tune_boost_pu = 0;
+               u16 pad2g_tune_pus = 0;
+
+               if (b43_nphy_ipa(dev)) {
+                       switch (phy->radio_rev) {
+                       case 9:
+                               txmix2g_tune_boost_pu = 0x0041;
+                               /* TODO */
+                               break;
+                       case 14:
+                               txmix2g_tune_boost_pu = 0x21;
+                               pad2g_tune_pus = 0x23;
+                               break;
+                       }
+               }
+
+               if (txmix2g_tune_boost_pu)
+                       b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0,
+                                       txmix2g_tune_boost_pu);
+               if (pad2g_tune_pus)
+                       b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0,
+                                       pad2g_tune_pus);
+               if (txmix2g_tune_boost_pu)
+                       b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1,
+                                       txmix2g_tune_boost_pu);
+               if (pad2g_tune_pus)
+                       b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1,
+                                       pad2g_tune_pus);
+       }
 
        usleep_range(50, 100);
 
@@ -690,13 +820,62 @@ static void b43_radio_2057_setup(struct b43_wldev *dev,
 static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       u16 saved_regs_phy[12];
+       u16 saved_regs_phy_rf[6];
+       u16 saved_regs_radio[2] = { };
+       static const u16 phy_to_store[] = {
+               B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2,
+               B43_NPHY_RFCTL_LUT_TRSW_LO1, B43_NPHY_RFCTL_LUT_TRSW_LO2,
+               B43_NPHY_RFCTL_RXG1, B43_NPHY_RFCTL_RXG2,
+               B43_NPHY_RFCTL_TXG1, B43_NPHY_RFCTL_TXG2,
+               B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4,
+               B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6,
+       };
+       static const u16 phy_to_store_rf[] = {
+               B43_NPHY_REV3_RFCTL_OVER0, B43_NPHY_REV3_RFCTL_OVER1,
+               B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4,
+               B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6,
+       };
        u16 tmp;
+       int i;
 
-       if (phy->radio_rev == 5) {
-               b43_phy_mask(dev, 0x342, ~0x2);
+       /* Save */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               saved_regs_phy[i] = b43_phy_read(dev, phy_to_store[i]);
+       for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++)
+               saved_regs_phy_rf[i] = b43_phy_read(dev, phy_to_store_rf[i]);
+
+       /* Set */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               b43_phy_write(dev, phy_to_store[i], 0);
+       b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER0, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER1, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0x007f);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0x007f);
+
+       switch (phy->radio_rev) {
+       case 5:
+               b43_phy_mask(dev, B43_NPHY_REV7_RF_CTL_OVER3, ~0x2);
                udelay(10);
                b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1);
-               b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1);
+               b43_radio_maskset(dev, R2057v7_IQTEST_SEL_PU2, ~0x2, 0x1);
+               break;
+       case 9:
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2);
+               saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU);
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x11);
+               break;
+       case 14:
+               saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU);
+               saved_regs_radio[1] = b43_radio_read(dev, R2057v7_IQTEST_SEL_PU2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2);
+               b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, 0x2);
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x1);
+               break;
        }
 
        /* Enable */
@@ -720,14 +899,30 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
        /* Disable */
        b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1);
 
-       if (phy->radio_rev == 5) {
-               b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1);
-               b43_radio_mask(dev, 0x1ca, ~0x2);
-       }
-       if (phy->radio_rev <= 4 || phy->radio_rev == 6) {
+       /* Restore */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++)
+               b43_phy_write(dev, phy_to_store_rf[i], saved_regs_phy_rf[i]);
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               b43_phy_write(dev, phy_to_store[i], saved_regs_phy[i]);
+
+       switch (phy->radio_rev) {
+       case 0 ... 4:
+       case 6:
                b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp);
                b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0,
                                  tmp << 2);
+               break;
+       case 5:
+               b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1);
+               b43_radio_mask(dev, R2057v7_IQTEST_SEL_PU2, ~0x2);
+               break;
+       case 9:
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]);
+               break;
+       case 14:
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]);
+               b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, saved_regs_radio[1]);
+               break;
        }
 
        return tmp & 0x3e;
@@ -749,7 +944,7 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0);
        } else {
                b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61);
-               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1);
+               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9);
        }
        b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
 
@@ -829,6 +1024,9 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev)
 {
        b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1);
 
+       if (0) /* FIXME: Is this BCM43217 specific? */
+               b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x2);
+
        b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78);
        b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80);
        mdelay(2);
@@ -1386,6 +1584,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
                                 u16 wait, bool iqmode, bool dac_test,
                                 bool modify_bbmult)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int i;
        u16 seq_mode;
@@ -1393,6 +1592,26 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
 
        b43_nphy_stay_in_carrier_search(dev, true);
 
+       if (phy->rev >= 7) {
+               bool lpf_bw3, lpf_bw4;
+
+               lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80;
+               lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80;
+
+               if (lpf_bw3 || lpf_bw4) {
+                       /* TODO */
+               } else {
+                       u16 value = b43_nphy_read_lpf_ctl(dev, 0);
+                       if (phy->rev >= 19)
+                               b43_nphy_rf_ctl_override_rev19(dev, 0x80, value,
+                                                              0, false, 1);
+                       else
+                               b43_nphy_rf_ctl_override_rev7(dev, 0x80, value,
+                                                             0, false, 1);
+                       nphy->lpf_bw_overrode_for_sample_play = true;
+               }
+       }
+
        if ((nphy->bb_mult_save & 0x80000000) == 0) {
                tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
                nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
@@ -1520,6 +1739,12 @@ static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale,
        }
 }
 
+static void b43_nphy_rssi_select_rev19(struct b43_wldev *dev, u8 code,
+                                      enum n_rssi_type rssi_type)
+{
+       /* TODO */
+}
+
 static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code,
                                      enum n_rssi_type rssi_type)
 {
@@ -1589,13 +1814,15 @@ static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code,
                                        enum ieee80211_band band =
                                                b43_current_band(dev->wl);
 
-                                       if (b43_nphy_ipa(dev))
-                                               val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
-                                       else
-                                               val = 0x11;
-                                       reg = (i == 0) ? 0x2000 : 0x3000;
-                                       reg |= B2055_PADDRV;
-                                       b43_radio_write(dev, reg, val);
+                                       if (dev->phy.rev < 7) {
+                                               if (b43_nphy_ipa(dev))
+                                                       val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
+                                               else
+                                                       val = 0x11;
+                                               reg = (i == 0) ? B2056_TX0 : B2056_TX1;
+                                               reg |= B2056_TX_TX_SSI_MUX;
+                                               b43_radio_write(dev, reg, val);
+                                       }
 
                                        reg = (i == 0) ?
                                                B43_NPHY_AFECTL_OVER1 :
@@ -1682,7 +1909,9 @@ static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code,
 static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code,
                                 enum n_rssi_type type)
 {
-       if (dev->phy.rev >= 3)
+       if (dev->phy.rev >= 19)
+               b43_nphy_rssi_select_rev19(dev, code, type);
+       else if (dev->phy.rev >= 3)
                b43_nphy_rev3_rssi_select(dev, code, type);
        else
                b43_nphy_rev2_rssi_select(dev, code, type);
@@ -1726,6 +1955,8 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
        u16 save_regs_phy[9];
        u16 s[2];
 
+       /* TODO: rev7+ is treated like rev3+, what about rev19+? */
+
        if (dev->phy.rev >= 3) {
                save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1);
                save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2);
@@ -1825,12 +2056,14 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
                B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
                B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
-               0x342, 0x343, 0x346, 0x347,
+               B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4,
+               B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6,
                0x2ff,
                B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1,
                B43_NPHY_RFCTL_CMD,
                B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
-               0x340, 0x341, 0x344, 0x345,
+               B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4,
+               B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6,
                B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
        };
        u16 *regs_to_store;
@@ -1877,9 +2110,24 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7);
 
        if (dev->phy.rev >= 7) {
-               /* TODO */
+               b43_nphy_rf_ctl_override_one_to_many(dev,
+                                                    N_RF_CTL_OVER_CMD_RXRF_PU,
+                                                    0, 0, false);
+               b43_nphy_rf_ctl_override_one_to_many(dev,
+                                                    N_RF_CTL_OVER_CMD_RX_PU,
+                                                    1, 0, false);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0);
                if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false,
+                                                     0);
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x10, 1, 0, false,
+                                                     0);
                } else {
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x10, 0, 0, false,
+                                                     0);
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x20, 1, 0, false,
+                                                     0);
                }
        } else {
                b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
@@ -1908,7 +2156,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                /* Grab RSSI results for every possible VCM */
                for (vcm = 0; vcm < 8; vcm++) {
                        if (dev->phy.rev >= 7)
-                               ;
+                               b43_radio_maskset(dev,
+                                                 core ? R2057_NB_MASTER_CORE1 :
+                                                        R2057_NB_MASTER_CORE0,
+                                                 ~R2057_VCM_MASK, vcm);
                        else
                                b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
                                                  0xE3, vcm << 2);
@@ -1939,7 +2190,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
                /* Select the best VCM */
                if (dev->phy.rev >= 7)
-                       ;
+                       b43_radio_maskset(dev,
+                                         core ? R2057_NB_MASTER_CORE1 :
+                                                R2057_NB_MASTER_CORE0,
+                                         ~R2057_VCM_MASK, vcm);
                else
                        b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
                                          0xE3, vcm_final << 2);
@@ -2009,6 +2263,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
        }
        if (dev->phy.rev >= 7) {
+               rssical_radio_regs[0] = b43_radio_read(dev,
+                                                      R2057_NB_MASTER_CORE0);
+               rssical_radio_regs[1] = b43_radio_read(dev,
+                                                      R2057_NB_MASTER_CORE1);
        } else {
                rssical_radio_regs[0] = b43_radio_read(dev, B2056_RX0 |
                                                       B2056_RX_RSSI_MISC);
@@ -2209,7 +2467,9 @@ static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, enum n_rssi_type type)
  */
 static void b43_nphy_rssi_cal(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 3) {
+       if (dev->phy.rev >= 19) {
+               /* TODO */
+       } else if (dev->phy.rev >= 3) {
                b43_nphy_rev3_rssi_cal(dev);
        } else {
                b43_nphy_rev2_rssi_cal(dev, N_RSSI_NB);
@@ -2222,7 +2482,21 @@ static void b43_nphy_rssi_cal(struct b43_wldev *dev)
  * Workarounds
  **************************************************/
 
-static void b43_nphy_gain_ctl_workarounds_rev3plus(struct b43_wldev *dev)
+static void b43_nphy_gain_ctl_workarounds_rev19(struct b43_wldev *dev)
+{
+       /* TODO */
+}
+
+static void b43_nphy_gain_ctl_workarounds_rev7(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+
+       switch (phy->rev) {
+       /* TODO */
+       }
+}
+
+static void b43_nphy_gain_ctl_workarounds_rev3(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -2419,22 +2693,16 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
 static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 7)
-               ; /* TODO */
+       if (dev->phy.rev >= 19)
+               b43_nphy_gain_ctl_workarounds_rev19(dev);
+       else if (dev->phy.rev >= 7)
+               b43_nphy_gain_ctl_workarounds_rev7(dev);
        else if (dev->phy.rev >= 3)
-               b43_nphy_gain_ctl_workarounds_rev3plus(dev);
+               b43_nphy_gain_ctl_workarounds_rev3(dev);
        else
                b43_nphy_gain_ctl_workarounds_rev1_2(dev);
 }
 
-/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */
-static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
-{
-       if (!offset)
-               offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
-       return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
-}
-
 static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3059,6 +3327,7 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
        b43_phy_set(dev, B43_NPHY_IQFLIP,
                    B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
 
+       /* TODO: rev19+ */
        if (dev->phy.rev >= 7)
                b43_nphy_workarounds_rev7plus(dev);
        else if (dev->phy.rev >= 3)
@@ -3120,6 +3389,7 @@ static void b43_nphy_update_txrx_chain(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */
 static void b43_nphy_stop_playback(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u16 tmp;
 
@@ -3140,6 +3410,15 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
                nphy->bb_mult_save = 0;
        }
 
+       if (phy->rev >= 7) {
+               if (phy->rev >= 19)
+                       b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true,
+                                                      1);
+               else
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1);
+               nphy->lpf_bw_overrode_for_sample_play = false;
+       }
+
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, 0);
 }
@@ -3149,16 +3428,23 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core,
                                        struct nphy_txgains target,
                                        struct nphy_iqcal_params *params)
 {
+       struct b43_phy *phy = &dev->phy;
        int i, j, indx;
        u16 gain;
 
        if (dev->phy.rev >= 3) {
+               params->tx_lpf = target.tx_lpf[core]; /* Rev 7+ */
                params->txgm = target.txgm[core];
                params->pga = target.pga[core];
                params->pad = target.pad[core];
                params->ipa = target.ipa[core];
-               params->cal_gain = (params->txgm << 12) | (params->pga << 8) |
-                                       (params->pad << 4) | (params->ipa);
+               if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 7) {
+                       params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa) | (params->tx_lpf << 15);
+               } else {
+                       params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa);
+               }
                for (j = 0; j < 5; j++)
                        params->ncorr[j] = 0x79;
        } else {
@@ -3199,6 +3485,7 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */
 static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u8 i;
        u16 bmask, val, tmp;
@@ -3268,12 +3555,25 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val);
 
                if (band == IEEE80211_BAND_5GHZ) {
-                       b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
-                                       ~B43_NPHY_TXPCTL_CMD_INIT, 0x64);
-                       if (dev->phy.rev > 1)
+                       if (phy->rev >= 19) {
+                               /* TODO */
+                       } else if (phy->rev >= 7) {
+                               b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+                                               ~B43_NPHY_TXPCTL_CMD_INIT,
+                                               0x32);
                                b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
                                                ~B43_NPHY_TXPCTL_INIT_PIDXI1,
+                                               0x32);
+                       } else {
+                               b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+                                               ~B43_NPHY_TXPCTL_CMD_INIT,
                                                0x64);
+                               if (phy->rev > 1)
+                                       b43_phy_maskset(dev,
+                                                       B43_NPHY_TXPCTL_INIT,
+                                                       ~B43_NPHY_TXPCTL_INIT_PIDXI1,
+                                                       0x64);
+                       }
                }
 
                if (dev->phy.rev >= 3) {
@@ -3290,6 +3590,10 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                        }
                }
 
+               if (phy->rev >= 7) {
+                       /* TODO */
+               }
+
                if (dev->phy.rev >= 3) {
                        b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100);
                        b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100);
@@ -3331,6 +3635,7 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, 1);
 
+       /* TODO: rev19+ */
        if (dev->phy.rev >= 7) {
                txpi[0] = txpi[1] = 30;
        } else if (dev->phy.rev >= 3) {
@@ -3369,7 +3674,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
        */
 
        for (i = 0; i < 2; i++) {
-               txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]);
+               const u32 *table = b43_nphy_get_tx_gain_table(dev);
+
+               if (!table)
+                       break;
+               txgain = *(table + txpi[i]);
 
                if (dev->phy.rev >= 3)
                        radio_gain = (txgain >> 16) & 0x1FFFF;
@@ -3429,7 +3738,9 @@ static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev)
        u8 core;
        u16 r; /* routing */
 
-       if (phy->rev >= 7) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
                for (core = 0; core < 2; core++) {
                        r = core ? 0x190 : 0x170;
                        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
@@ -3517,7 +3828,9 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
        if (b43_nphy_ipa(dev))
                b43_nphy_ipa_internal_tssi_setup(dev);
 
-       if (phy->rev >= 7)
+       if (phy->rev >= 19)
+               b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, false, 0);
+       else if (phy->rev >= 7)
                b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, false, 0);
        else if (phy->rev >= 3)
                b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false);
@@ -3527,14 +3840,20 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
        udelay(20);
        tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1);
        b43_nphy_stop_playback(dev);
+
        b43_nphy_rssi_select(dev, 0, N_RSSI_W1);
 
-       if (phy->rev >= 7)
+       if (phy->rev >= 19)
+               b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, true, 0);
+       else if (phy->rev >= 7)
                b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, true, 0);
        else if (phy->rev >= 3)
                b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true);
 
-       if (phy->rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+               return;
+       } else if (phy->rev >= 3) {
                nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF;
                nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF;
        } else {
@@ -3726,7 +4045,9 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
                udelay(1);
        }
 
-       if (dev->phy.rev >= 7) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
                                ~B43_NPHY_TXPCTL_CMD_INIT, 0x19);
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
@@ -3783,27 +4104,36 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
        int i;
 
        table = b43_nphy_get_tx_gain_table(dev);
+       if (!table)
+               return;
+
        b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
        b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
 
-       if (phy->rev >= 3) {
+       if (phy->rev < 3)
+               return;
+
 #if 0
-               nphy->gmval = (table[0] >> 16) & 0x7000;
+       nphy->gmval = (table[0] >> 16) & 0x7000;
 #endif
 
-               for (i = 0; i < 128; i++) {
+       for (i = 0; i < 128; i++) {
+               if (phy->rev >= 19) {
+                       /* TODO */
+                       return;
+               } else if (phy->rev >= 7) {
+                       /* TODO */
+                       return;
+               } else {
                        pga_gain = (table[i] >> 24) & 0xF;
                        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-                               rfpwr_offset =
-                                b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
+                               rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
                        else
-                               rfpwr_offset =
-                                0; /* FIXME */
-                       b43_ntab_write(dev, B43_NTAB32(26, 576 + i),
-                                      rfpwr_offset);
-                       b43_ntab_write(dev, B43_NTAB32(27, 576 + i),
-                                      rfpwr_offset);
+                               rfpwr_offset = 0; /* FIXME */
                }
+
+               b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset);
+               b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset);
        }
 }
 
@@ -3820,7 +4150,9 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
                nphy->rfctrl_intc2_save = b43_phy_read(dev,
                                                       B43_NPHY_RFCTL_INTC2);
                band = b43_current_band(dev->wl);
-               if (dev->phy.rev >= 3) {
+               if (dev->phy.rev >= 7) {
+                       tmp = 0x1480;
+               } else if (dev->phy.rev >= 3) {
                        if (band == IEEE80211_BAND_5GHZ)
                                tmp = 0x600;
                        else
@@ -4267,7 +4599,13 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
        }
 
-       if (dev->phy.rev >= 7) {
+       if (dev->phy.rev >= 19) {
+               /* TODO */
+       } else if (dev->phy.rev >= 7) {
+               b43_radio_maskset(dev, R2057_NB_MASTER_CORE0, ~R2057_VCM_MASK,
+                                 rssical_radio_regs[0]);
+               b43_radio_maskset(dev, R2057_NB_MASTER_CORE1, ~R2057_VCM_MASK,
+                                 rssical_radio_regs[1]);
        } else {
                b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3,
                                  rssical_radio_regs[0]);
@@ -4291,15 +4629,78 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]);
 }
 
+static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev)
+{
+       /* TODO */
+}
+
+static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = dev->phy.n;
+       u16 *save = nphy->tx_rx_cal_radio_saveregs;
+       int core, off;
+       u16 r, tmp;
+
+       for (core = 0; core < 2; core++) {
+               r = core ? 0x20 : 0;
+               off = core * 11;
+
+               save[off + 0] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MASTER);
+               save[off + 1] = b43_radio_read(dev, r + R2057_TX0_IQCAL_VCM_HG);
+               save[off + 2] = b43_radio_read(dev, r + R2057_TX0_IQCAL_IDAC);
+               save[off + 3] = b43_radio_read(dev, r + R2057_TX0_TSSI_VCM);
+               save[off + 4] = 0;
+               save[off + 5] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MUX);
+               if (phy->radio_rev != 5)
+                       save[off + 6] = b43_radio_read(dev, r + R2057_TX0_TSSIA);
+               save[off + 7] = b43_radio_read(dev, r + R2057_TX0_TSSIG);
+               save[off + 8] = b43_radio_read(dev, r + R2057_TX0_TSSI_MISC1);
+
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0xA);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55);
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0);
+                       b43_radio_write(dev, r + R2057_TX0_TSSIG, 0);
+                       if (nphy->use_int_tx_iq_lo_cal) {
+                               b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x4);
+                               tmp = true ? 0x31 : 0x21; /* TODO */
+                               b43_radio_write(dev, r + R2057_TX0_TSSIA, tmp);
+                       }
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0x00);
+               } else {
+                       b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0x6);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55);
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0);
+
+                       if (phy->radio_rev != 5)
+                               b43_radio_write(dev, r + R2057_TX0_TSSIA, 0);
+                       if (nphy->use_int_tx_iq_lo_cal) {
+                               b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x6);
+                               tmp = true ? 0x31 : 0x21; /* TODO */
+                               b43_radio_write(dev, r + R2057_TX0_TSSIG, tmp);
+                       }
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0);
+               }
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */
 static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u16 *save = nphy->tx_rx_cal_radio_saveregs;
        u16 tmp;
        u8 offset, i;
 
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               b43_nphy_tx_cal_radio_setup_rev19(dev);
+       } else if (phy->rev >= 7) {
+               b43_nphy_tx_cal_radio_setup_rev7(dev);
+       } else if (phy->rev >= 3) {
            for (i = 0; i < 2; i++) {
                tmp = (i == 0) ? 0x2000 : 0x3000;
                offset = i * 11;
@@ -4464,7 +4865,13 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
                        b43_nphy_stay_in_carrier_search(dev, false);
 
                for (i = 0; i < 2; ++i) {
-                       if (dev->phy.rev >= 3) {
+                       if (dev->phy.rev >= 7) {
+                               target.ipa[i] = curr_gain[i] & 0x0007;
+                               target.pad[i] = (curr_gain[i] & 0x00F8) >> 3;
+                               target.pga[i] = (curr_gain[i] & 0x0F00) >> 8;
+                               target.txgm[i] = (curr_gain[i] & 0x7000) >> 12;
+                               target.tx_lpf[i] = (curr_gain[i] & 0x8000) >> 15;
+                       } else if (dev->phy.rev >= 3) {
                                target.ipa[i] = curr_gain[i] & 0x000F;
                                target.pad[i] = (curr_gain[i] & 0x00F0) >> 4;
                                target.pga[i] = (curr_gain[i] & 0x0F00) >> 8;
@@ -4488,7 +4895,16 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
 
                for (i = 0; i < 2; ++i) {
                        table = b43_nphy_get_tx_gain_table(dev);
-                       if (dev->phy.rev >= 3) {
+                       if (!table)
+                               break;
+
+                       if (dev->phy.rev >= 7) {
+                               target.ipa[i] = (table[index[i]] >> 16) & 0x7;
+                               target.pad[i] = (table[index[i]] >> 19) & 0x1F;
+                               target.pga[i] = (table[index[i]] >> 24) & 0xF;
+                               target.txgm[i] = (table[index[i]] >> 28) & 0x7;
+                               target.tx_lpf[i] = (table[index[i]] >> 31) & 0x1;
+                       } else if (dev->phy.rev >= 3) {
                                target.ipa[i] = (table[index[i]] >> 16) & 0xF;
                                target.pad[i] = (table[index[i]] >> 20) & 0xF;
                                target.pga[i] = (table[index[i]] >> 24) & 0xF;
@@ -4537,6 +4953,8 @@ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */
 static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = dev->phy.n;
        u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs;
        u16 tmp;
 
@@ -4568,7 +4986,12 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
                regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
 
-               b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, 1, 3);
+               if (!nphy->use_int_tx_iq_lo_cal)
+                       b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA,
+                                                     1, 3);
+               else
+                       b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA,
+                                                     0, 3);
                b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1);
                b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2);
 
@@ -4576,6 +4999,33 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
                b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001);
                b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001);
+
+               tmp = b43_nphy_read_lpf_ctl(dev, 0);
+               if (phy->rev >= 19)
+                       b43_nphy_rf_ctl_override_rev19(dev, 0x80, tmp, 0, false,
+                                                      1);
+               else if (phy->rev >= 7)
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x80, tmp, 0, false,
+                                                     1);
+
+               if (nphy->use_int_tx_iq_lo_cal && true /* FIXME */) {
+                       if (phy->rev >= 19) {
+                               b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3,
+                                                              false, 0);
+                       } else if (phy->rev >= 8) {
+                               b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3,
+                                                             false, 0);
+                       } else if (phy->rev == 7) {
+                               b43_radio_maskset(dev, R2057_OVR_REG0, 1 << 4, 1 << 4);
+                               if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+                                       b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE0, ~1, 0);
+                                       b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE1, ~1, 0);
+                               } else {
+                                       b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE0, ~1, 0);
+                                       b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE1, ~1, 0);
+                               }
+                       }
+               }
        } else {
                b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000);
                b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000);
@@ -4604,6 +5054,7 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */
 static void b43_nphy_save_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
@@ -4628,7 +5079,26 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
 
        b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs);
        /* TODO use some definitions */
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               txcal_radio_regs[0] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_FINE_I);
+               txcal_radio_regs[1] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_FINE_Q);
+               txcal_radio_regs[4] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_COARSE_I);
+               txcal_radio_regs[5] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_COARSE_Q);
+               txcal_radio_regs[2] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_FINE_I);
+               txcal_radio_regs[3] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_FINE_Q);
+               txcal_radio_regs[6] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_COARSE_I);
+               txcal_radio_regs[7] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_COARSE_Q);
+       } else if (phy->rev >= 3) {
                txcal_radio_regs[0] = b43_radio_read(dev, 0x2021);
                txcal_radio_regs[1] = b43_radio_read(dev, 0x2022);
                txcal_radio_regs[2] = b43_radio_read(dev, 0x3021);
@@ -4655,6 +5125,7 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
 static void b43_nphy_restore_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 coef[4];
@@ -4702,7 +5173,26 @@ static void b43_nphy_restore_cal(struct b43_wldev *dev)
        }
 
        /* TODO use some definitions */
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               b43_radio_write(dev, R2057_TX0_LOFT_FINE_I,
+                               txcal_radio_regs[0]);
+               b43_radio_write(dev, R2057_TX0_LOFT_FINE_Q,
+                               txcal_radio_regs[1]);
+               b43_radio_write(dev, R2057_TX0_LOFT_COARSE_I,
+                               txcal_radio_regs[4]);
+               b43_radio_write(dev, R2057_TX0_LOFT_COARSE_Q,
+                               txcal_radio_regs[5]);
+               b43_radio_write(dev, R2057_TX1_LOFT_FINE_I,
+                               txcal_radio_regs[2]);
+               b43_radio_write(dev, R2057_TX1_LOFT_FINE_Q,
+                               txcal_radio_regs[3]);
+               b43_radio_write(dev, R2057_TX1_LOFT_COARSE_I,
+                               txcal_radio_regs[6]);
+               b43_radio_write(dev, R2057_TX1_LOFT_COARSE_Q,
+                               txcal_radio_regs[7]);
+       } else if (phy->rev >= 3) {
                b43_radio_write(dev, 0x2021, txcal_radio_regs[0]);
                b43_radio_write(dev, 0x2022, txcal_radio_regs[1]);
                b43_radio_write(dev, 0x3021, txcal_radio_regs[2]);
@@ -4779,7 +5269,13 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                }
        }
 
-       b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AD9);
+       } else {
+               b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
+       }
 
        if (!b43_is_40mhz(dev))
                freq = 2500;
@@ -5173,6 +5669,9 @@ static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev,
 static int b43_nphy_cal_rx_iq(struct b43_wldev *dev,
                        struct nphy_txgains target, u8 type, bool debug)
 {
+       if (dev->phy.rev >= 7)
+               type = 0;
+
        if (dev->phy.rev >= 3)
                return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug);
        else
@@ -5259,6 +5758,9 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */
 static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init)
 {
+       if (dev->phy.rev >= 7)
+               return;
+
        if (dev->phy.rev >= 3) {
                if (!init)
                        return;
@@ -5334,6 +5836,10 @@ static int b43_phy_initn(struct b43_wldev *dev)
 #endif
                }
        }
+       nphy->use_int_tx_iq_lo_cal = b43_nphy_ipa(dev) ||
+               phy->rev >= 7 ||
+               (phy->rev >= 5 &&
+                sprom->boardflags2_hi & B43_BFH2_INTERNDET_TXIQCAL);
        nphy->deaf_count = 0;
        b43_nphy_tables_init(dev);
        nphy->crsminpwr_adjusted = false;
@@ -5343,6 +5849,16 @@ static int b43_phy_initn(struct b43_wldev *dev)
        if (dev->phy.rev >= 3) {
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0);
                b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0);
+               if (phy->rev >= 7) {
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0);
+               }
+               if (phy->rev >= 19) {
+                       /* TODO */
+               }
+
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0);
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0);
        } else {
@@ -5380,7 +5896,9 @@ static int b43_phy_initn(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50);
        b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30);
 
-       b43_nphy_update_mimo_config(dev, nphy->preamble_override);
+       if (phy->rev < 8)
+               b43_nphy_update_mimo_config(dev, nphy->preamble_override);
+
        b43_nphy_update_txrx_chain(dev);
 
        if (phy->rev < 2) {
@@ -5412,10 +5930,12 @@ static int b43_phy_initn(struct b43_wldev *dev)
 
        b43_mac_phy_clock_set(dev, true);
 
-       b43_nphy_pa_override(dev, false);
-       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
-       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
-       b43_nphy_pa_override(dev, true);
+       if (phy->rev < 7) {
+               b43_nphy_pa_override(dev, false);
+               b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
+               b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+               b43_nphy_pa_override(dev, true);
+       }
 
        b43_nphy_classifier(dev, 0, 0);
        b43_nphy_read_clip_detection(dev, clip);
@@ -5538,23 +6058,23 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
        struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int ch = new_channel->hw_value;
-
-       u16 old_band_5ghz;
        u16 tmp16;
 
-       old_band_5ghz =
-               b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ;
-       if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) {
+       if (new_channel->band == IEEE80211_BAND_5GHZ) {
                tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4);
-               b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000);
+               /* Put BPHY in the reset */
+               b43_phy_set(dev, B43_PHY_B_BBCFG,
+                           B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16);
                b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ);
-       } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) {
+       } else if (new_channel->band == IEEE80211_BAND_2GHZ) {
                b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ);
                tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4);
-               b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF);
+               /* Take BPHY out of the reset */
+               b43_phy_mask(dev, B43_PHY_B_BBCFG,
+                            (u16)~(B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX));
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16);
        }
 
@@ -5579,31 +6099,45 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
 
        if (dev->phy.rev >= 3 &&
            dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) {
-               bool avoid = false;
+               u8 spuravoid = 0;
+
                if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) {
-                       avoid = true;
-               } else if (!b43_is_40mhz(dev)) {
-                       if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
-                               avoid = true;
-               } else { /* 40MHz */
-                       if (nphy->aband_spurwar_en &&
-                           (ch == 38 || ch == 102 || ch == 118))
-                               avoid = dev->dev->chip_id == 0x4716;
+                       spuravoid = 1;
+               } else if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 18) {
+                       /* TODO */
+               } else if (phy->rev >= 17) {
+                       /* TODO: Off for channels 1-11, but check 12-14! */
+               } else if (phy->rev >= 16) {
+                       /* TODO: Off for 2 GHz, but check 5 GHz! */
+               } else if (phy->rev >= 7) {
+                       if (!b43_is_40mhz(dev)) { /* 20MHz */
+                               if (ch == 13 || ch == 14 || ch == 153)
+                                       spuravoid = 1;
+                       } else { /* 40 MHz */
+                               if (ch == 54)
+                                       spuravoid = 1;
+                       }
+               } else {
+                       if (!b43_is_40mhz(dev)) { /* 20MHz */
+                               if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
+                                       spuravoid = 1;
+                       } else { /* 40MHz */
+                               if (nphy->aband_spurwar_en &&
+                                   (ch == 38 || ch == 102 || ch == 118))
+                                       spuravoid = dev->dev->chip_id == 0x4716;
+                       }
                }
 
-               b43_nphy_pmu_spur_avoid(dev, avoid);
+               b43_nphy_pmu_spur_avoid(dev, spuravoid);
 
-               if (dev->dev->chip_id == 43222 || dev->dev->chip_id == 43224 ||
-                   dev->dev->chip_id == 43225) {
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW,
-                                   avoid ? 0x5341 : 0x8889);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-               }
+               b43_mac_switch_freq(dev, spuravoid);
 
                if (dev->phy.rev == 3 || dev->phy.rev == 4)
                        ; /* TODO: reset PLL */
 
-               if (avoid)
+               if (spuravoid)
                        b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX);
                else
                        b43_phy_mask(dev, B43_NPHY_BBCFG,
@@ -5634,7 +6168,10 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
 
        u8 tmp;
 
-       if (phy->rev >= 7) {
+       if (phy->rev >= 19) {
+               return -ESRCH;
+               /* TODO */
+       } else if (phy->rev >= 7) {
                r2057_get_chantabent_rev7(dev, channel->center_freq,
                                          &tabent_r7, &tabent_r7_2g);
                if (!tabent_r7 && !tabent_r7_2g)
@@ -5671,7 +6208,9 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
                        b43_phy_mask(dev, 0x310, (u16)~0x8000);
        }
 
-       if (phy->rev >= 7) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
                const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ?
                        &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs);
 
@@ -5824,7 +6363,7 @@ static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
 static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 {
        /* Register 1 is a 32-bit register. */
-       B43_WARN_ON(reg == 1);
+       B43_WARN_ON(dev->phy.rev < 7 && reg == 1);
 
        if (dev->phy.rev >= 7)
                reg |= 0x200; /* Radio 0x2057 */
@@ -5838,7 +6377,7 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
 {
        /* Register 1 is a 32-bit register. */
-       B43_WARN_ON(reg == 1);
+       B43_WARN_ON(dev->phy.rev < 7 && reg == 1);
 
        b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg);
        b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
@@ -5848,15 +6387,23 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
 static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                                        bool blocked)
 {
+       struct b43_phy *phy = &dev->phy;
+
        if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
                b43err(dev->wl, "MAC not suspended\n");
 
        if (blocked) {
-               b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
-                               ~B43_NPHY_RFCTL_CMD_CHIP0PU);
-               if (dev->phy.rev >= 7) {
+               if (phy->rev >= 19) {
                        /* TODO */
-               } else if (dev->phy.rev >= 3) {
+               } else if (phy->rev >= 8) {
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
+                                    ~B43_NPHY_RFCTL_CMD_CHIP0PU);
+               } else if (phy->rev >= 7) {
+                       /* Nothing needed */
+               } else if (phy->rev >= 3) {
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
+                                    ~B43_NPHY_RFCTL_CMD_CHIP0PU);
+
                        b43_radio_mask(dev, 0x09, ~0x2);
 
                        b43_radio_write(dev, 0x204D, 0);
@@ -5874,11 +6421,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                        b43_radio_write(dev, 0x3064, 0);
                }
        } else {
-               if (dev->phy.rev >= 7) {
+               if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 7) {
                        if (!dev->phy.radio_on)
                                b43_radio_2057_init(dev);
                        b43_switch_channel(dev, dev->phy.channel);
-               } else if (dev->phy.rev >= 3) {
+               } else if (phy->rev >= 3) {
                        if (!dev->phy.radio_on)
                                b43_radio_init2056(dev);
                        b43_switch_channel(dev, dev->phy.channel);
@@ -5891,10 +6440,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */
 static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on)
 {
+       struct b43_phy *phy = &dev->phy;
        u16 override = on ? 0x0 : 0x7FFF;
        u16 core = on ? 0xD : 0x00FD;
 
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 3) {
                if (on) {
                        b43_phy_write(dev, B43_NPHY_AFECTL_C1, core);
                        b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override);
index ecfbf66dbc3b7fdacc8d3e07dbf8d1476355fd16..30bec815b969651ee4ad1cca2c6c74e607cc834f 100644 (file)
 #define B43_NPHY_TXF_40CO_B1S0                 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */
 #define B43_NPHY_TXF_40CO_B32S1                        B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */
 #define B43_NPHY_TXF_40CO_B1S1                 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */
+#define B43_NPHY_REV3_RFCTL_OVER0              B43_PHY_N(0x0E7)
 #define B43_NPHY_TXF_40CO_B32S2                        B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */
 #define B43_NPHY_TXF_40CO_B1S2                 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */
 #define B43_NPHY_BIST_STAT2                    B43_PHY_N(0x0EA) /* BIST status 2 */
 #define B43_NPHY_BIST_STAT3                    B43_PHY_N(0x0EB) /* BIST status 3 */
 #define B43_NPHY_RFCTL_OVER                    B43_PHY_N(0x0EC) /* RF control override */
+#define B43_NPHY_REV3_RFCTL_OVER1              B43_PHY_N(0x0EC)
 #define B43_NPHY_MIMOCFG                       B43_PHY_N(0x0ED) /* MIMO config */
 #define  B43_NPHY_MIMOCFG_GFMIX                        0x0004 /* Greenfield or mixed mode */
 #define  B43_NPHY_MIMOCFG_AUTO                 0x0100 /* Greenfield/mixed mode auto */
 #define B43_NPHY_REV3_C2_CLIP2_GAIN_A          B43_PHY_N(0x2AF)
 #define B43_NPHY_REV3_C2_CLIP2_GAIN_B          B43_PHY_N(0x2B0)
 
+#define B43_NPHY_REV7_RF_CTL_MISC_REG3         B43_PHY_N(0x340)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG4         B43_PHY_N(0x341)
+#define B43_NPHY_REV7_RF_CTL_OVER3             B43_PHY_N(0x342)
+#define B43_NPHY_REV7_RF_CTL_OVER4             B43_PHY_N(0x343)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG5         B43_PHY_N(0x344)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG6         B43_PHY_N(0x345)
+#define B43_NPHY_REV7_RF_CTL_OVER5             B43_PHY_N(0x346)
+#define B43_NPHY_REV7_RF_CTL_OVER6             B43_PHY_N(0x347)
+
 #define B43_PHY_B_BBCFG                                B43_PHY_N_BMODE(0x001) /* BB config */
+#define  B43_PHY_B_BBCFG_RSTCCA                        0x4000 /* Reset CCA */
+#define  B43_PHY_B_BBCFG_RSTRX                 0x8000 /* Reset RX */
 #define B43_PHY_B_TEST                         B43_PHY_N_BMODE(0x00A)
 
 struct b43_wldev;
@@ -935,6 +948,8 @@ struct b43_phy_n {
        bool gain_boost;
        bool elna_gain_config;
        bool band5g_pwrgain;
+       bool use_int_tx_iq_lo_cal;
+       bool lpf_bw_overrode_for_sample_play;
 
        u8 mphase_cal_phase_id;
        u16 mphase_txcal_cmdidx;
index df3574545819d7f34249f59206ef0d3b1f40a473..ff1e026a61a131ffce5b0d51b22aaf79619b6be2 100644 (file)
@@ -105,6 +105,27 @@ static u16 r2057_rev8_init[][2] = {
 };
 */
 
+/* Extracted from MMIO dump of 6.30.223.141 */
+static u16 r2057_rev9_init[][2] = {
+       { 0x27, 0x1f }, { 0x28, 0x0a }, { 0x29, 0x2f }, { 0x42, 0x1f },
+       { 0x48, 0x3f }, { 0x5c, 0x41 }, { 0x63, 0x14 }, { 0x64, 0x12 },
+       { 0x66, 0xff }, { 0x74, 0xa3 }, { 0x7b, 0x14 }, { 0x7c, 0x14 },
+       { 0x7d, 0xee }, { 0x86, 0xc0 }, { 0xc4, 0x10 }, { 0xc9, 0x01 },
+       { 0xe1, 0x41 }, { 0xe8, 0x14 }, { 0xe9, 0x12 }, { 0xeb, 0xff },
+       { 0xf5, 0x0a }, { 0xf8, 0x09 }, { 0xf9, 0xa3 }, { 0x100, 0x14 },
+       { 0x101, 0x10 }, { 0x102, 0xee }, { 0x10b, 0xc0 }, { 0x149, 0x10 },
+       { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 },
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static u16 r2057_rev14_init[][2] = {
+       { 0x011, 0xfc }, { 0x030, 0x24 }, { 0x040, 0x1c }, { 0x082, 0x08 },
+       { 0x0b4, 0x44 }, { 0x0c8, 0x01 }, { 0x0c9, 0x01 }, { 0x107, 0x08 },
+       { 0x14d, 0x01 }, { 0x14e, 0x01 }, { 0x1af, 0x40 }, { 0x1b0, 0x40 },
+       { 0x1cc, 0x01 }, { 0x1cf, 0x10 }, { 0x1d0, 0x0f }, { 0x1d3, 0x10 },
+       { 0x1d4, 0x0f },
+};
+
 #define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
                   r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
                   r20, r21, r22, r23, r24, r25, r26, r27) \
@@ -137,6 +158,27 @@ static u16 r2057_rev8_init[][2] = {
        .radio_lna2g_tune_core1                 = r26,  \
        .radio_lna5g_tune_core1                 = r27
 
+#define RADIOREGS7_2G(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
+                     r10, r11, r12, r13, r14, r15, r16, r17) \
+       .radio_vcocal_countval0                 = r00,  \
+       .radio_vcocal_countval1                 = r01,  \
+       .radio_rfpll_refmaster_sparextalsize    = r02,  \
+       .radio_rfpll_loopfilter_r1              = r03,  \
+       .radio_rfpll_loopfilter_c2              = r04,  \
+       .radio_rfpll_loopfilter_c1              = r05,  \
+       .radio_cp_kpd_idac                      = r06,  \
+       .radio_rfpll_mmd0                       = r07,  \
+       .radio_rfpll_mmd1                       = r08,  \
+       .radio_vcobuf_tune                      = r09,  \
+       .radio_logen_mx2g_tune                  = r10,  \
+       .radio_logen_indbuf2g_tune              = r11,  \
+       .radio_txmix2g_tune_boost_pu_core0      = r12,  \
+       .radio_pad2g_tune_pus_core0             = r13,  \
+       .radio_lna2g_tune_core0                 = r14,  \
+       .radio_txmix2g_tune_boost_pu_core1      = r15,  \
+       .radio_pad2g_tune_pus_core1             = r16,  \
+       .radio_lna2g_tune_core1                 = r17
+
 #define PHYREGS(r0, r1, r2, r3, r4, r5)        \
        .phy_regs.phy_bw1a      = r0,   \
        .phy_regs.phy_bw2       = r1,   \
@@ -145,6 +187,353 @@ static u16 r2057_rev8_init[][2] = {
        .phy_regs.phy_bw5       = r4,   \
        .phy_regs.phy_bw6       = r5
 
+/* Copied from brcmsmac (5.75.11): chan_info_nphyrev8_2057_rev5 */
+static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev5[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61,
+                             0x03, 0xff),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61,
+                             0x03, 0xff),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61,
+                             0x03, 0xef),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b,
+                             0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61,
+                             0x03, 0xdf),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80,
+                             0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61,
+                             0x03, 0xcf),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85,
+                             0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61,
+                             0x03, 0xbf),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61,
+                             0x03, 0xaf),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61,
+                             0x03, 0x9f),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61,
+                             0x03, 0x8f),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99,
+                             0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61,
+                             0x03, 0x7f),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e,
+                             0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61,
+                             0x03, 0x6f),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 2467,
+               RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3,
+                             0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61,
+                             0x03, 0x5f),
+               PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b),
+       },
+       {
+               .freq                   = 2472,
+               RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8,
+                             0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61,
+                             0x03, 0x4f),
+               PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429),
+       },
+       {
+               .freq                   = 2484,
+               RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4,
+                             0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61,
+                             0x03, 0x3f),
+               PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424),
+       }
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio_rev14[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7_2G(0x48, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x6c,
+                             0x09, 0x0d, 0x09, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x71,
+                             0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x76,
+                             0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7_2G(0x52, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x7b,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7_2G(0x55, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x80,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7_2G(0x58, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x85,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8a,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8f,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7_2G(0x62, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x94,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7_2G(0x66, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x99,
+                             0x09, 0x0b, 0x07, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7_2G(0x69, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x9e,
+                             0x09, 0x0b, 0x07, 0x03, 0x01, 0x43, 0xff, 0x01,
+                             0x43, 0xff),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c,
+                          0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71,
+                          0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76,
+                          0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b,
+                          0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80,
+                          0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85,
+                          0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99,
+                          0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e,
+                          0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 5180,
+               RADIOREGS7(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x9f, 0x2f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x4f,
+                          0x3a, 0x83, 0x00, 0xfc),
+               PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb),
+       },
+       {
+               .freq                   = 5200,
+               RADIOREGS7(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x7f, 0x2f, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x4c,
+                          0x4a, 0x83, 0x00, 0xf8),
+               PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9),
+       },
+       {
+               .freq                   = 5220,
+               RADIOREGS7(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x6d, 0x3d, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x2d,
+                          0x2a, 0x73, 0x00, 0xf8),
+               PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7),
+       },
+       {
+               .freq                   = 5240,
+               RADIOREGS7(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c,
+                          0x02, 0x0d, 0x00, 0x0d, 0x00, 0x8d, 0x00, 0x00,
+                          0x4d, 0x1c, 0x73, 0x00, 0xf8, 0x00, 0x00, 0x4d,
+                          0x2b, 0x73, 0x00, 0xf8),
+               PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5),
+       },
+       {
+               .freq                   = 5745,
+               RADIOREGS7(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x08, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x06,
+                          0x02, 0x03, 0x00, 0x30),
+               PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5765,
+               RADIOREGS7(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+                          0x02, 0x03, 0x00, 0x00),
+               PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5785,
+               RADIOREGS7(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x08, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+                          0x21, 0x03, 0x00, 0x00),
+               PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5805,
+               RADIOREGS7(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89,
+                          0x04, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00,
+                          0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+                          0x00, 0x03, 0x00, 0x00),
+               PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4),
+       },
+       {
+               .freq                   = 5825,
+               RADIOREGS7(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d,
+                          0x04, 0x07, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00,
+                          0x05, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+                          0x00, 0x03, 0x00, 0x00),
+               PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3),
+       },
+};
+
 void r2057_upload_inittabs(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
@@ -171,6 +560,18 @@ void r2057_upload_inittabs(struct b43_wldev *dev)
                        size = ARRAY_SIZE(r2057_rev5a_init);
                }
                break;
+       case 16:
+               if (phy->radio_rev == 9) {
+                       table = r2057_rev9_init[0];
+                       size = ARRAY_SIZE(r2057_rev9_init);
+               }
+               break;
+       case 17:
+               if (phy->radio_rev == 14) {
+                       table = r2057_rev14_init[0];
+                       size = ARRAY_SIZE(r2057_rev14_init);
+               }
+               break;
        }
 
        B43_WARN_ON(!table);
@@ -193,8 +594,25 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
        *tabent_r7 = NULL;
        *tabent_r7_2g = NULL;
 
-       /* TODO */
        switch (phy->rev) {
+       case 8:
+               if (phy->radio_rev == 5) {
+                       e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5);
+               }
+               break;
+       case 16:
+               if (phy->radio_rev == 9) {
+                       e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9);
+               }
+               break;
+       case 17:
+               if (phy->radio_rev == 14) {
+                       e_r7_2g = b43_nphy_chantab_phy_rev17_radio_rev14;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev17_radio_rev14);
+               }
+               break;
        default:
                break;
        }
index 675d1bb64429d432762e50af57daed10a38d1d31..220d080238ff8805816eef0b127d990045fa1082 100644 (file)
@@ -84,6 +84,8 @@
 #define R2057_CMOSBUF_RX_RCCR                  0x04c
 #define R2057_LOGEN_SEL_PKDET                  0x04d
 #define R2057_CMOSBUF_SHAREIQ_PTAT             0x04e
+
+/* MISC core 0 */
 #define R2057_RXTXBIAS_CONFIG_CORE0            0x04f
 #define R2057_TXGM_TXRF_PUS_CORE0              0x050
 #define R2057_TXGM_IDAC_BLEED_CORE0            0x051
 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0  0x0d1
 #define R2057_LPF_GAIN_CORE0                   0x0d2
 #define R2057_DACBUF_IDACS_BW_CORE0            0x0d3
+
+/* MISC core 1 */
 #define R2057_RXTXBIAS_CONFIG_CORE1            0x0d4
 #define R2057_TXGM_TXRF_PUS_CORE1              0x0d5
 #define R2057_TXGM_IDAC_BLEED_CORE1            0x0d6
 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1  0x156
 #define R2057_LPF_GAIN_CORE1                   0x157
 #define R2057_DACBUF_IDACS_BW_CORE1            0x158
+
 #define R2057_DACBUF_VINCM_CORE1               0x159
 #define R2057_RCCAL_START_R1_Q1_P1             0x15a
 #define R2057_RCCAL_X1                         0x15b
 #define R2057_RCCAL_BCAP_VAL                   0x16b
 #define R2057_RCCAL_HPC_VAL                    0x16c
 #define R2057_RCCAL_OVERRIDES                  0x16d
+
+/* TX core 0 */
 #define R2057_TX0_IQCAL_GAIN_BW                        0x170
 #define R2057_TX0_LOFT_FINE_I                  0x171
 #define R2057_TX0_LOFT_FINE_Q                  0x172
 #define R2057_TX0_TXRXCOUPLE_2G_PWRUP          0x17e
 #define R2057_TX0_TXRXCOUPLE_5G_ATTEN          0x17f
 #define R2057_TX0_TXRXCOUPLE_5G_PWRUP          0x180
+
+/* TX core 1 */
 #define R2057_TX1_IQCAL_GAIN_BW                        0x190
 #define R2057_TX1_LOFT_FINE_I                  0x191
 #define R2057_TX1_LOFT_FINE_Q                  0x192
 #define R2057_TX1_TXRXCOUPLE_2G_PWRUP          0x19e
 #define R2057_TX1_TXRXCOUPLE_5G_ATTEN          0x19f
 #define R2057_TX1_TXRXCOUPLE_5G_PWRUP          0x1a0
+
 #define R2057_AFE_VCM_CAL_MASTER_CORE0         0x1a1
 #define R2057_AFE_SET_VCM_I_CORE0              0x1a2
 #define R2057_AFE_SET_VCM_Q_CORE0              0x1a3
index b2217159292648bfc6f83a2693fe63769152367b..ab27c2de2f43a5e590f88f98e796dfe052e0310c 100644 (file)
@@ -2146,7 +2146,196 @@ static const u16 b43_ntab_antswctl_r3[4][32] = {
        }
 };
 
-/* TX gain tables */
+/* static tables, PHY revision >= 7 */
+
+/* Copied from brcmsmac (5.75.11) */
+static const u32 b43_ntab_tmap_r7[] = {
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+       0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+       0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+       0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00011111,
+       0x11110000, 0x1111f111, 0x11111111, 0x111111f1,
+       0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa,
+       0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88,
+       0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888,
+       0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808,
+       0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08,
+       0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080,
+       0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+       0x22000000, 0x2222b222, 0x22222222, 0x222222b2,
+       0xb2222220, 0x22222222, 0x22d22222, 0x00000222,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x33000000, 0x3333b333, 0x33333333, 0x333333b3,
+       0xb3333330, 0x33333333, 0x33d33333, 0x00000333,
+       0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+       0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+       0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+       0x22222200, 0x2222f222, 0x22222222, 0x222222f2,
+       0x22222222, 0x22222222, 0x22f22222, 0x00000222,
+       0x11000000, 0x1111f111, 0x11111111, 0x11111111,
+       0xf1111111, 0x11111111, 0x11f11111, 0x01111111,
+       0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b,
+       0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb,
+       0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa,
+       0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb,
+       0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999,
+       0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88,
+       0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a,
+       0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b,
+       0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909,
+       0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08,
+       0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a,
+       0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090,
+       0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090,
+       0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080,
+       0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0,
+       0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+       0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+       0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+       0x33000000, 0x3333f333, 0x33333333, 0x333333f3,
+       0xf3333330, 0x33333333, 0x33f33333, 0x00000333,
+       0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+       0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+       0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+       0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+       0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_noisevar_r7[] = {
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+};
+
+/**************************************************
+ * TX gain tables
+ **************************************************/
+
 static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
        0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42,
        0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44,
@@ -2182,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
        0x03801442, 0x03801344, 0x03801342, 0x00002b00,
 };
 
-static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
+/* EPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = {
        0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e,
        0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037,
        0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e,
@@ -2217,7 +2408,44 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
        0x1041003c, 0x1041003b, 0x10410039, 0x10410037,
 };
 
-static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev3_hi_pwr_2g[] = {
+       0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e,
+       0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037,
+       0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e,
+       0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037,
+       0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e,
+       0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037,
+       0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e,
+       0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037,
+       0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e,
+       0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037,
+       0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e,
+       0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037,
+       0x09410044, 0x09410042, 0x09410040, 0x0941003e,
+       0x0941003c, 0x0941003b, 0x09410039, 0x09410037,
+       0x08410044, 0x08410042, 0x08410040, 0x0841003e,
+       0x0841003c, 0x0841003b, 0x08410039, 0x08410037,
+       0x07410044, 0x07410042, 0x07410040, 0x0741003e,
+       0x0741003c, 0x0741003b, 0x07410039, 0x07410037,
+       0x06410044, 0x06410042, 0x06410040, 0x0641003e,
+       0x0641003c, 0x0641003b, 0x06410039, 0x06410037,
+       0x05410044, 0x05410042, 0x05410040, 0x0541003e,
+       0x0541003c, 0x0541003b, 0x05410039, 0x05410037,
+       0x04410044, 0x04410042, 0x04410040, 0x0441003e,
+       0x0441003c, 0x0441003b, 0x04410039, 0x04410037,
+       0x03410044, 0x03410042, 0x03410040, 0x0341003e,
+       0x0341003c, 0x0341003b, 0x03410039, 0x03410037,
+       0x02410044, 0x02410042, 0x02410040, 0x0241003e,
+       0x0241003c, 0x0241003b, 0x02410039, 0x02410037,
+       0x01410044, 0x01410042, 0x01410040, 0x0141003e,
+       0x0141003c, 0x0141003b, 0x01410039, 0x01410037,
+       0x00410044, 0x00410042, 0x00410040, 0x0041003e,
+       0x0041003c, 0x0041003b, 0x00410039, 0x00410037
+};
+
+/* EPA 5 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = {
        0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e,
        0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037,
        0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e,
@@ -2252,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
        0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037,
 };
 
-static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = {
        0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e,
        0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037,
        0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e,
@@ -2287,7 +2515,42 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
        0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034,
 };
 
-static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev4_hi_pwr_5g[] = {
+       0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e,
+       0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037,
+       0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e,
+       0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037,
+       0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e,
+       0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037,
+       0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e,
+       0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037,
+       0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e,
+       0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037,
+       0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e,
+       0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037,
+       0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e,
+       0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037,
+       0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e,
+       0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037,
+       0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e,
+       0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037,
+       0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e,
+       0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037,
+       0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e,
+       0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037,
+       0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e,
+       0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038,
+       0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e,
+       0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037,
+       0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e,
+       0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037,
+       0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e,
+       0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037,
+       0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c,
+       0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034
+};
+
+static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = {
        0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044,
        0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c,
        0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e,
@@ -2322,7 +2585,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
        0x0062003b, 0x00620039, 0x00620037, 0x00620035,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa[] = {
+/* IPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = {
        0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029,
        0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025,
        0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029,
@@ -2357,7 +2622,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = {
        0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = {
        0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029,
        0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025,
        0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029,
@@ -2392,7 +2657,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
        0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = {
        0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029,
        0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025,
        0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029,
@@ -2427,7 +2692,117 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
        0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
+/* Copied from brcmsmac (5.75.11): nphy_tpc_txgain_ipa_2g_2057rev5 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = {
+       0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e,
+       0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033,
+       0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e,
+       0x3067002e, 0x305f002f, 0x30570030, 0x3057002d,
+       0x304f002e, 0x30470031, 0x3047002e, 0x3047002c,
+       0x30470029, 0x303f002c, 0x303f0029, 0x3037002d,
+       0x3037002a, 0x30370028, 0x302f002c, 0x302f002a,
+       0x302f0028, 0x302f0026, 0x3027002c, 0x30270029,
+       0x30270027, 0x30270025, 0x30270023, 0x301f002c,
+       0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024,
+       0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b,
+       0x30170028, 0x30170026, 0x30170024, 0x30170022,
+       0x30170020, 0x3017001e, 0x3017001d, 0x3017001b,
+       0x3017001a, 0x30170018, 0x30170017, 0x30170015,
+       0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024,
+       0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d,
+       0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017,
+       0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215,
+       0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = {
+       0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029,
+       0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b,
+       0x6087002e, 0x60770031, 0x606f0032, 0x60670034,
+       0x60670031, 0x605f0033, 0x605f0031, 0x60570033,
+       0x60570030, 0x6057002d, 0x6057002b, 0x604f002d,
+       0x604f002b, 0x604f0029, 0x604f0026, 0x60470029,
+       0x60470027, 0x603f0029, 0x603f0027, 0x603f0025,
+       0x60370029, 0x60370027, 0x60370024, 0x602f002a,
+       0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a,
+       0x60270028, 0x60270026, 0x60270024, 0x60270022,
+       0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024,
+       0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d,
+       0x60170029, 0x60170027, 0x60170025, 0x60170023,
+       0x60170021, 0x6017001f, 0x6017001d, 0x6017001c,
+       0x6017001a, 0x60170018, 0x60170018, 0x60170016,
+       0x60170015, 0x600f0029, 0x600f0027, 0x600f0025,
+       0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d,
+       0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018,
+       0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215,
+       0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev14_2g[] = {
+       0x50df002e, 0x50cf002d, 0x50bf002c, 0x50b7002b,
+       0x50af002a, 0x50a70029, 0x509f0029, 0x50970028,
+       0x508f0027, 0x50870027, 0x507f0027, 0x50770027,
+       0x506f0027, 0x50670027, 0x505f0028, 0x50570029,
+       0x504f002b, 0x5047002e, 0x5047002b, 0x50470029,
+       0x503f002c, 0x503f0029, 0x5037002c, 0x5037002a,
+       0x50370028, 0x502f002d, 0x502f002b, 0x502f0028,
+       0x502f0026, 0x5027002d, 0x5027002a, 0x50270028,
+       0x50270026, 0x50270024, 0x501f002e, 0x501f002b,
+       0x501f0029, 0x501f0027, 0x501f0024, 0x501f0022,
+       0x501f0020, 0x501f001f, 0x5017002c, 0x50170029,
+       0x50170027, 0x50170024, 0x50170022, 0x50170021,
+       0x5017001f, 0x5017001d, 0x5017001b, 0x5017001a,
+       0x50170018, 0x50170017, 0x50170015, 0x500f002c,
+       0x500f002a, 0x500f0027, 0x500f0025, 0x500f0023,
+       0x500f0022, 0x500f001f, 0x500f001e, 0x500f001c,
+       0x500f001a, 0x500f0019, 0x500f0018, 0x500f0016,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+};
+
+/* IPA 2 5Hz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = {
        0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031,
        0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b,
        0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027,
@@ -2462,6 +2837,42 @@ static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
        0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f,
 };
 
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = {
+       0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f,
+       0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030,
+       0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032,
+       0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030,
+       0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b,
+       0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b,
+       0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029,
+       0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032,
+       0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031,
+       0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031,
+       0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030,
+       0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f,
+       0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d,
+       0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d,
+       0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c,
+       0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023,
+       0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c,
+       0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016,
+       0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012,
+       0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e,
+       0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b,
+       0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008,
+       0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007,
+       0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006,
+       0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004,
+       0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003,
+       0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+       0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+       0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+       0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+       0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001,
+       0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001,
+};
+
 const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = {
        -114, -108, -98, -91, -84, -78, -70, -62,
        -54, -46, -39, -31, -23, -15, -8, 0
@@ -3031,6 +3442,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset,
                b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
        } while (0)
 
+static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev)
+{
+       ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
+       ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
+       ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
+       ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
+       ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
+       ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
+       ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+}
+
+static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev)
+{
+       struct ssb_sprom *sprom = dev->dev->bus_sprom;
+       u8 antswlut;
+       int core, offset, i;
+
+       const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */
+       const u8 antswlut0_values[][3] = {
+               { 0x2, 0x12, 0x8 }, /* Core 0 */
+               { 0x2, 0x18, 0x2 }, /* Core 1 */
+       };
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+               antswlut = sprom->fem.ghz5.antswlut;
+       else
+               antswlut = sprom->fem.ghz2.antswlut;
+
+       switch (antswlut) {
+       case 0:
+               for (core = 0; core < 2; core++) {
+                       for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) {
+                               offset = core ? 0x20 : 0x00;
+                               offset += antswlut0_offsets[i];
+                               b43_ntab_write(dev, B43_NTAB8(9, offset),
+                                              antswlut0_values[core][i]);
+                       }
+               }
+               break;
+       default:
+               b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut);
+               break;
+       }
+}
+
+static void b43_nphy_tables_init_rev16(struct b43_wldev *dev)
+{
+       /* Static tables */
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+               b43_nphy_tables_init_shared_lut(dev);
+       }
+
+       /* Volatile tables */
+       b43_nphy_tables_init_rev7_volatile(dev);
+}
+
+static void b43_nphy_tables_init_rev7(struct b43_wldev *dev)
+{
+       /* Static tables */
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
+               ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
+               ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7);
+               ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
+               ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+               ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
+               ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
+               ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
+               ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
+               b43_nphy_tables_init_shared_lut(dev);
+       }
+
+       /* Volatile tables */
+       b43_nphy_tables_init_rev7_volatile(dev);
+}
+
 static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3057,16 +3553,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
                ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
                ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
                ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
-               ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
-               ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
-               ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
-               ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
-               ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
-               ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
-               ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+               b43_nphy_tables_init_shared_lut(dev);
        }
 
        /* Volatile tables */
@@ -3115,7 +3602,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */
 void b43_nphy_tables_init(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 3)
+       if (dev->phy.rev >= 16)
+               b43_nphy_tables_init_rev16(dev);
+       else if (dev->phy.rev >= 7)
+               b43_nphy_tables_init_rev7(dev);
+       else if (dev->phy.rev >= 3)
                b43_nphy_tables_init_rev3(dev);
        else
                b43_nphy_tables_init_rev0(dev);
@@ -3124,23 +3615,55 @@ void b43_nphy_tables_init(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */
 static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
+
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-               if (dev->phy.rev >= 6) {
-                       if (dev->dev->chip_id == 47162)
-                               return txpwrctrl_tx_gain_ipa_rev5;
-                       return txpwrctrl_tx_gain_ipa_rev6;
-               } else if (dev->phy.rev >= 5) {
-                       return txpwrctrl_tx_gain_ipa_rev5;
-               } else {
-                       return txpwrctrl_tx_gain_ipa;
+               switch (phy->rev) {
+               case 17:
+                       if (phy->radio_rev == 14)
+                               return b43_ntab_tx_gain_ipa_2057_rev14_2g;
+                       break;
+               case 16:
+                       if (phy->radio_rev == 9)
+                               return b43_ntab_tx_gain_ipa_2057_rev9_2g;
+                       break;
+               case 8:
+                       if (phy->radio_rev == 5)
+                               return b43_ntab_tx_gain_ipa_2057_rev5_2g;
+                       break;
+               case 6:
+                       if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162)
+                               return b43_ntab_tx_gain_ipa_rev5_2g;
+                       return b43_ntab_tx_gain_ipa_rev6_2g;
+               case 5:
+                       return b43_ntab_tx_gain_ipa_rev5_2g;
+               case 4:
+               case 3:
+                       return b43_ntab_tx_gain_ipa_rev3_2g;
                }
+
+               b43err(dev->wl,
+                      "No 2GHz IPA gain table available for this device\n");
+               return NULL;
        } else {
-               return txpwrctrl_tx_gain_ipa_5g;
+               switch (phy->rev) {
+               case 16:
+                       if (phy->radio_rev == 9)
+                               return b43_ntab_tx_gain_ipa_2057_rev9_5g;
+                       break;
+               case 3 ... 6:
+                       return b43_ntab_tx_gain_ipa_rev3_5g;
+               }
+
+               b43err(dev->wl,
+                      "No 5GHz IPA gain table available for this device\n");
+               return NULL;
        }
 }
 
 const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        enum ieee80211_band band = b43_current_band(dev->wl);
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -3152,19 +3675,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
            (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) {
                return b43_nphy_get_ipa_gain_table(dev);
        } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-               if (dev->phy.rev == 3)
-                       return b43_ntab_tx_gain_rev3_5ghz;
-               if (dev->phy.rev == 4)
+               switch (phy->rev) {
+               case 6:
+               case 5:
+                       return b43_ntab_tx_gain_epa_rev5_5g;
+               case 4:
                        return sprom->fem.ghz5.extpa_gain == 3 ?
-                               b43_ntab_tx_gain_rev4_5ghz :
-                               b43_ntab_tx_gain_rev4_5ghz; /* FIXME */
-               else
-                       return b43_ntab_tx_gain_rev5plus_5ghz;
+                               b43_ntab_tx_gain_epa_rev4_5g :
+                               b43_ntab_tx_gain_epa_rev4_hi_pwr_5g;
+               case 3:
+                       return b43_ntab_tx_gain_epa_rev3_5g;
+               default:
+                       b43err(dev->wl,
+                              "No 5GHz EPA gain table available for this device\n");
+                       return NULL;
+               }
        } else {
-               if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3)
-                       return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */
-               else
-                       return b43_ntab_tx_gain_rev3plus_2ghz;
+               switch (phy->rev) {
+               case 6:
+               case 5:
+                       if (sprom->fem.ghz5.extpa_gain == 3)
+                               return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g;
+                       /* fall through */
+               case 4:
+               case 3:
+                       return b43_ntab_tx_gain_epa_rev3_2g;
+               default:
+                       b43err(dev->wl,
+                              "No 2GHz EPA gain table available for this device\n");
+                       return NULL;
+               }
        }
 }
 
index 3a58aee4c4cf714aa72bc22a8c85670b135eb934..3ce2e6f3a2781d168cf0955f6246b24a1d5d1181 100644 (file)
@@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
 #define B43_NTAB_C1_LOFEEDTH_R3                B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */
 #define B43_NTAB_C1_PAPD_COMP_R3       B43_NTAB16(27, 576)
 
+/* Static N-PHY tables, PHY revision >= 7 */
+#define B43_NTAB_TMAP_R7               B43_NTAB32(12,   0) /* TM AP */
+#define B43_NTAB_NOISEVAR_R7           B43_NTAB32(16,   0) /* noise variance */
+
 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE       18
 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE       18
 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE      18
index 4cffb2ee36738106d5543c4687f8406f4b3e1847..de0cff3df38950bd3194180d3f964990ce36d723 100644 (file)
@@ -34,6 +34,7 @@ brcmfmac-objs += \
                dhd_common.o \
                dhd_linux.o \
                firmware.o \
+               feature.o \
                btcoex.o \
                vendor.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
index a16e644e7c08826fed5c6d75a6413b8ff5959bdd..f467cafe3e8f0803459807c03a13f9ab0fc19576 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/core.h>
 #include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/platform_device.h>
@@ -979,18 +978,20 @@ out:
        return ret;
 }
 
+#define BRCMF_SDIO_DEVICE(dev_id)      \
+       {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)}
+
 /* devices we support, null terminated */
 static const struct sdio_device_id brcmf_sdmmc_ids[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
-                    SDIO_DEVICE_ID_BROADCOM_4335_4339)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)},
-       { /* end: all zeroes */ },
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID),
+       { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
 
index c7c9f15c0fe08170ee2f953e9690a13e605a3291..96800db0536b578922cc8bf8aea924ede640cadc 100644 (file)
@@ -482,30 +482,30 @@ static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
 static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
 {
        switch (ci->pub.chip) {
-       case BCM4329_CHIP_ID:
+       case BRCM_CC_4329_CHIP_ID:
                ci->pub.ramsize = BCM4329_RAMSIZE;
                break;
-       case BCM43143_CHIP_ID:
+       case BRCM_CC_43143_CHIP_ID:
                ci->pub.ramsize = BCM43143_RAMSIZE;
                break;
-       case BCM43241_CHIP_ID:
+       case BRCM_CC_43241_CHIP_ID:
                ci->pub.ramsize = 0x90000;
                break;
-       case BCM4330_CHIP_ID:
+       case BRCM_CC_4330_CHIP_ID:
                ci->pub.ramsize = 0x48000;
                break;
-       case BCM4334_CHIP_ID:
+       case BRCM_CC_4334_CHIP_ID:
                ci->pub.ramsize = 0x80000;
                break;
-       case BCM4335_CHIP_ID:
+       case BRCM_CC_4335_CHIP_ID:
                ci->pub.ramsize = 0xc0000;
                ci->pub.rambase = 0x180000;
                break;
-       case BCM43362_CHIP_ID:
+       case BRCM_CC_43362_CHIP_ID:
                ci->pub.ramsize = 0x3c000;
                break;
-       case BCM4339_CHIP_ID:
-       case BCM4354_CHIP_ID:
+       case BRCM_CC_4339_CHIP_ID:
+       case BRCM_CC_4354_CHIP_ID:
                ci->pub.ramsize = 0xc0000;
                ci->pub.rambase = 0x180000;
                break;
@@ -682,7 +682,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
                  ci->pub.chiprev);
 
        if (socitype == SOCI_SB) {
-               if (ci->pub.chip != BCM4329_CHIP_ID) {
+               if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
                        brcmf_err("SB chip is not supported\n");
                        return -ENODEV;
                }
@@ -1008,13 +1008,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
        chip = container_of(pub, struct brcmf_chip_priv, pub);
 
        switch (pub->chip) {
-       case BCM4354_CHIP_ID:
+       case BRCM_CC_4354_CHIP_ID:
                /* explicitly check SR engine enable bit */
                pmu_cc3_mask = BIT(2);
                /* fall-through */
-       case BCM43241_CHIP_ID:
-       case BCM4335_CHIP_ID:
-       case BCM4339_CHIP_ID:
+       case BRCM_CC_43241_CHIP_ID:
+       case BRCM_CC_4335_CHIP_ID:
+       case BRCM_CC_4339_CHIP_ID:
                /* read PMU chipcontrol register 3 */
                addr = CORE_CC_REG(base, chipcontrol_addr);
                chip->ops->write32(chip->ctx, addr, 3);
index a8998eb60d22166eef9c51f027c5b7d41d2ae020..7da6441bcfa8e8cb519cf19379b9553a431fe50a 100644 (file)
@@ -103,6 +103,10 @@ struct brcmf_pub {
 
        struct brcmf_ampdu_rx_reorder
                *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
+
+       u32 feat_flags;
+       u32 chip_quirks;
+
 #ifdef DEBUG
        struct dentry *dbgfs_dir;
 #endif
@@ -175,7 +179,6 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
 void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
-u32 brcmf_get_chip_info(struct brcmf_if *ifp);
 void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success);
 
index 03fe8aca4d3261c81f704f89853364f0f0bb7507..be9f4f829192cc020294f08950b0fe995fca10dc 100644 (file)
@@ -41,37 +41,12 @@ void brcmf_debugfs_exit(void)
        root_folder = NULL;
 }
 
-static
-ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data,
-                                  size_t count, loff_t *ppos)
+static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
 {
-       struct brcmf_pub *drvr = f->private_data;
-       struct brcmf_bus *bus = drvr->bus_if;
-       char buf[40];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n",
-                       bus->chip, bus->chip, bus->chiprev);
-       return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_chipinfo_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_chipinfo_read
-};
-
-static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr)
-{
-       struct dentry *dentry =  drvr->dbgfs_dir;
+       struct brcmf_bus *bus = dev_get_drvdata(seq->private);
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr,
-                                   &brcmf_debugfs_chipinfo_ops);
+       seq_printf(seq, "chip: %x(%u) rev %u\n",
+                  bus->chip, bus->chip, bus->chiprev);
        return 0;
 }
 
@@ -83,7 +58,8 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
                return -ENODEV;
 
        drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
-       brcmf_debugfs_create_chipinfo(drvr);
+       brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
+
        return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
 }
 
@@ -98,148 +74,44 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
        return drvr->dbgfs_dir;
 }
 
-static
-ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data,
-                                       size_t count, loff_t *ppos)
-{
-       struct brcmf_sdio_count *sdcnt = f->private_data;
-       char buf[750];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf),
-                       "intrcount:    %u\nlastintrs:    %u\n"
-                       "pollcnt:      %u\nregfails:     %u\n"
-                       "tx_sderrs:    %u\nfcqueued:     %u\n"
-                       "rxrtx:        %u\nrx_toolong:   %u\n"
-                       "rxc_errors:   %u\nrx_hdrfail:   %u\n"
-                       "rx_badhdr:    %u\nrx_badseq:    %u\n"
-                       "fc_rcvd:      %u\nfc_xoff:      %u\n"
-                       "fc_xon:       %u\nrxglomfail:   %u\n"
-                       "rxglomframes: %u\nrxglompkts:   %u\n"
-                       "f2rxhdrs:     %u\nf2rxdata:     %u\n"
-                       "f2txdata:     %u\nf1regdata:    %u\n"
-                       "tickcnt:      %u\ntx_ctlerrs:   %lu\n"
-                       "tx_ctlpkts:   %lu\nrx_ctlerrs:   %lu\n"
-                       "rx_ctlpkts:   %lu\nrx_readahead: %lu\n",
-                       sdcnt->intrcount, sdcnt->lastintrs,
-                       sdcnt->pollcnt, sdcnt->regfails,
-                       sdcnt->tx_sderrs, sdcnt->fcqueued,
-                       sdcnt->rxrtx, sdcnt->rx_toolong,
-                       sdcnt->rxc_errors, sdcnt->rx_hdrfail,
-                       sdcnt->rx_badhdr, sdcnt->rx_badseq,
-                       sdcnt->fc_rcvd, sdcnt->fc_xoff,
-                       sdcnt->fc_xon, sdcnt->rxglomfail,
-                       sdcnt->rxglomframes, sdcnt->rxglompkts,
-                       sdcnt->f2rxhdrs, sdcnt->f2rxdata,
-                       sdcnt->f2txdata, sdcnt->f1regdata,
-                       sdcnt->tickcnt, sdcnt->tx_ctlerrs,
-                       sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
-                       sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
-
-       return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_sdio_counter_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_sdio_counter_read
+struct brcmf_debugfs_entry {
+       int (*read)(struct seq_file *seq, void *data);
+       struct brcmf_pub *drvr;
 };
 
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
-                                    struct brcmf_sdio_count *sdcnt)
+static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
 {
-       struct dentry *dentry = drvr->dbgfs_dir;
+       struct brcmf_debugfs_entry *entry = inode->i_private;
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("counters", S_IRUGO, dentry,
-                                   sdcnt, &brcmf_debugfs_sdio_counter_ops);
-}
-
-static
-ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
-                                    size_t count, loff_t *ppos)
-{
-       struct brcmf_fws_stats *fwstats = f->private_data;
-       char buf[650];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf),
-                       "header_pulls:      %u\n"
-                       "header_only_pkt:   %u\n"
-                       "tlv_parse_failed:  %u\n"
-                       "tlv_invalid_type:  %u\n"
-                       "mac_update_fails:  %u\n"
-                       "ps_update_fails:   %u\n"
-                       "if_update_fails:   %u\n"
-                       "pkt2bus:           %u\n"
-                       "generic_error:     %u\n"
-                       "rollback_success:  %u\n"
-                       "rollback_failed:   %u\n"
-                       "delayq_full:       %u\n"
-                       "supprq_full:       %u\n"
-                       "txs_indicate:      %u\n"
-                       "txs_discard:       %u\n"
-                       "txs_suppr_core:    %u\n"
-                       "txs_suppr_ps:      %u\n"
-                       "txs_tossed:        %u\n"
-                       "txs_host_tossed:   %u\n"
-                       "bus_flow_block:    %u\n"
-                       "fws_flow_block:    %u\n"
-                       "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
-                       "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
-                       fwstats->header_pulls,
-                       fwstats->header_only_pkt,
-                       fwstats->tlv_parse_failed,
-                       fwstats->tlv_invalid_type,
-                       fwstats->mac_update_failed,
-                       fwstats->mac_ps_update_failed,
-                       fwstats->if_update_failed,
-                       fwstats->pkt2bus,
-                       fwstats->generic_error,
-                       fwstats->rollback_success,
-                       fwstats->rollback_failed,
-                       fwstats->delayq_full_error,
-                       fwstats->supprq_full_error,
-                       fwstats->txs_indicate,
-                       fwstats->txs_discard,
-                       fwstats->txs_supp_core,
-                       fwstats->txs_supp_ps,
-                       fwstats->txs_tossed,
-                       fwstats->txs_host_tossed,
-                       fwstats->bus_flow_block,
-                       fwstats->fws_flow_block,
-                       fwstats->send_pkts[0], fwstats->send_pkts[1],
-                       fwstats->send_pkts[2], fwstats->send_pkts[3],
-                       fwstats->send_pkts[4],
-                       fwstats->requested_sent[0],
-                       fwstats->requested_sent[1],
-                       fwstats->requested_sent[2],
-                       fwstats->requested_sent[3],
-                       fwstats->requested_sent[4]);
-
-       return simple_read_from_buffer(data, count, ppos, buf, res);
+       return single_open(f, entry->read, entry->drvr->bus_if->dev);
 }
 
-static const struct file_operations brcmf_debugfs_fws_stats_ops = {
+static const struct file_operations brcmf_debugfs_def_ops = {
        .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_fws_stats_read
+       .open = brcmf_debugfs_entry_open,
+       .release = single_release,
+       .read = seq_read,
+       .llseek = seq_lseek
 };
 
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                   struct brcmf_fws_stats *stats)
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data))
 {
        struct dentry *dentry =  drvr->dbgfs_dir;
+       struct brcmf_debugfs_entry *entry;
+
+       if (IS_ERR_OR_NULL(dentry))
+               return -ENOENT;
+
+       entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->read = read_fn;
+       entry->drvr = drvr;
+
+       dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
+                                    &brcmf_debugfs_def_ops);
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("fws_stats", S_IRUGO, dentry,
-                                   stats, &brcmf_debugfs_fws_stats_ops);
+       return PTR_ERR_OR_ZERO(dentry);
 }
index ef52ed7abc69349640081292f5f862277900607c..6eade7c60c635542d893d8224e614ec88cc81392 100644 (file)
@@ -100,68 +100,6 @@ do {                                                                       \
 
 extern int brcmf_msg_level;
 
-/*
- * hold counter variables used in brcmfmac sdio driver.
- */
-struct brcmf_sdio_count {
-       uint intrcount;         /* Count of device interrupt callbacks */
-       uint lastintrs;         /* Count as of last watchdog timer */
-       uint pollcnt;           /* Count of active polls */
-       uint regfails;          /* Count of R_REG failures */
-       uint tx_sderrs;         /* Count of tx attempts with sd errors */
-       uint fcqueued;          /* Tx packets that got queued */
-       uint rxrtx;             /* Count of rtx requests (NAK to dongle) */
-       uint rx_toolong;        /* Receive frames too long to receive */
-       uint rxc_errors;        /* SDIO errors when reading control frames */
-       uint rx_hdrfail;        /* SDIO errors on header reads */
-       uint rx_badhdr;         /* Bad received headers (roosync?) */
-       uint rx_badseq;         /* Mismatched rx sequence number */
-       uint fc_rcvd;           /* Number of flow-control events received */
-       uint fc_xoff;           /* Number which turned on flow-control */
-       uint fc_xon;            /* Number which turned off flow-control */
-       uint rxglomfail;        /* Failed deglom attempts */
-       uint rxglomframes;      /* Number of glom frames (superframes) */
-       uint rxglompkts;        /* Number of packets from glom frames */
-       uint f2rxhdrs;          /* Number of header reads */
-       uint f2rxdata;          /* Number of frame data reads */
-       uint f2txdata;          /* Number of f2 frame writes */
-       uint f1regdata;         /* Number of f1 register accesses */
-       uint tickcnt;           /* Number of watchdog been schedule */
-       ulong tx_ctlerrs;       /* Err of sending ctrl frames */
-       ulong tx_ctlpkts;       /* Ctrl frames sent to dongle */
-       ulong rx_ctlerrs;       /* Err of processing rx ctrl frames */
-       ulong rx_ctlpkts;       /* Ctrl frames processed from dongle */
-       ulong rx_readahead_cnt; /* packets where header read-ahead was used */
-};
-
-struct brcmf_fws_stats {
-       u32 tlv_parse_failed;
-       u32 tlv_invalid_type;
-       u32 header_only_pkt;
-       u32 header_pulls;
-       u32 pkt2bus;
-       u32 send_pkts[5];
-       u32 requested_sent[5];
-       u32 generic_error;
-       u32 mac_update_failed;
-       u32 mac_ps_update_failed;
-       u32 if_update_failed;
-       u32 packet_request_failed;
-       u32 credit_request_failed;
-       u32 rollback_success;
-       u32 rollback_failed;
-       u32 delayq_full_error;
-       u32 supprq_full_error;
-       u32 txs_indicate;
-       u32 txs_discard;
-       u32 txs_supp_core;
-       u32 txs_supp_ps;
-       u32 txs_tossed;
-       u32 txs_host_tossed;
-       u32 bus_flow_block;
-       u32 fws_flow_block;
-};
-
 struct brcmf_pub;
 #ifdef DEBUG
 void brcmf_debugfs_init(void);
@@ -169,10 +107,8 @@ void brcmf_debugfs_exit(void);
 int brcmf_debugfs_attach(struct brcmf_pub *drvr);
 void brcmf_debugfs_detach(struct brcmf_pub *drvr);
 struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
-                                    struct brcmf_sdio_count *sdcnt);
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                   struct brcmf_fws_stats *stats);
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data));
 #else
 static inline void brcmf_debugfs_init(void)
 {
@@ -187,9 +123,11 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
 static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
 {
 }
-static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                                 struct brcmf_fws_stats *stats)
+static inline
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data))
 {
+       return 0;
 }
 #endif
 
index 09dd8c13d8448392372299c6eb8954132fca9c72..bf448081d6fb98d569f1848b0e665db5a8cd9d91 100644 (file)
@@ -30,6 +30,7 @@
 #include "wl_cfg80211.h"
 #include "fwil.h"
 #include "fwsignal.h"
+#include "feature.h"
 #include "proto.h"
 
 MODULE_AUTHOR("Broadcom Corporation");
@@ -936,6 +937,8 @@ int brcmf_bus_start(struct device *dev)
        if (ret < 0)
                goto fail;
 
+       brcmf_feat_attach(drvr);
+
        ret = brcmf_fws_init(drvr);
        if (ret < 0)
                goto fail;
@@ -1073,16 +1076,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
        return !err;
 }
 
-/*
- * return chip id and rev of the device encoded in u32.
- */
-u32 brcmf_get_chip_info(struct brcmf_if *ifp)
-{
-       struct brcmf_bus *bus = ifp->drvr->bus_if;
-
-       return bus->chip << 4 | bus->chiprev;
-}
-
 static void brcmf_driver_register(struct work_struct *work)
 {
 #ifdef CONFIG_BRCMFMAC_SDIO
index 8fa0dbbbda72b6ed1d1dc84020594287cf595a37..67d91d5cc13d85de1e5c0395842c90f13d54cb16 100644 (file)
@@ -391,6 +391,40 @@ struct brcmf_sdio_hdrinfo {
        u16 tail_pad;
 };
 
+/*
+ * hold counter variables
+ */
+struct brcmf_sdio_count {
+       uint intrcount;         /* Count of device interrupt callbacks */
+       uint lastintrs;         /* Count as of last watchdog timer */
+       uint pollcnt;           /* Count of active polls */
+       uint regfails;          /* Count of R_REG failures */
+       uint tx_sderrs;         /* Count of tx attempts with sd errors */
+       uint fcqueued;          /* Tx packets that got queued */
+       uint rxrtx;             /* Count of rtx requests (NAK to dongle) */
+       uint rx_toolong;        /* Receive frames too long to receive */
+       uint rxc_errors;        /* SDIO errors when reading control frames */
+       uint rx_hdrfail;        /* SDIO errors on header reads */
+       uint rx_badhdr;         /* Bad received headers (roosync?) */
+       uint rx_badseq;         /* Mismatched rx sequence number */
+       uint fc_rcvd;           /* Number of flow-control events received */
+       uint fc_xoff;           /* Number which turned on flow-control */
+       uint fc_xon;            /* Number which turned off flow-control */
+       uint rxglomfail;        /* Failed deglom attempts */
+       uint rxglomframes;      /* Number of glom frames (superframes) */
+       uint rxglompkts;        /* Number of packets from glom frames */
+       uint f2rxhdrs;          /* Number of header reads */
+       uint f2rxdata;          /* Number of frame data reads */
+       uint f2txdata;          /* Number of f2 frame writes */
+       uint f1regdata;         /* Number of f1 register accesses */
+       uint tickcnt;           /* Number of watchdog been schedule */
+       ulong tx_ctlerrs;       /* Err of sending ctrl frames */
+       ulong tx_ctlpkts;       /* Ctrl frames sent to dongle */
+       ulong rx_ctlerrs;       /* Err of processing rx ctrl frames */
+       ulong rx_ctlpkts;       /* Ctrl frames processed from dongle */
+       ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
 /* misc chip info needed by some of the routines */
 /* Private data for SDIO bus interaction */
 struct brcmf_sdio {
@@ -620,40 +654,46 @@ enum brcmf_firmware_type {
        name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
 
 static const struct brcmf_firmware_names brcmf_fwname_data[] = {
-       { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
-       { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
-       { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
-       { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
-       { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
-       { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
-       { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
-       { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
-       { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
-       { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
+       { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
+       { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
+       { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
+       { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
+       { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
+       { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
+       { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
+       { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
+       { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+       { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 };
 
-static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
-                                        enum brcmf_firmware_type type)
+static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
+                                 struct brcmf_sdio_dev *sdiodev)
 {
        int i;
 
        for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
                if (brcmf_fwname_data[i].chipid == ci->chip &&
-                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
-                       switch (type) {
-                       case BRCMF_FIRMWARE_BIN:
-                               return brcmf_fwname_data[i].bin;
-                       case BRCMF_FIRMWARE_NVRAM:
-                               return brcmf_fwname_data[i].nv;
-                       default:
-                               brcmf_err("invalid firmware type (%d)\n", type);
-                               return NULL;
-                       }
-               }
+                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
+                       break;
        }
-       brcmf_err("Unknown chipid %d [%d]\n",
-                 ci->chip, ci->chiprev);
-       return NULL;
+
+       if (i == ARRAY_SIZE(brcmf_fwname_data)) {
+               brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
+               return -ENODEV;
+       }
+
+       /* check if firmware path is provided by module parameter */
+       if (brcmf_firmware_path[0] != '\0') {
+               if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/')
+                       strcat(brcmf_firmware_path, "/");
+
+               strcpy(sdiodev->fw_name, brcmf_firmware_path);
+               strcpy(sdiodev->nvram_name, brcmf_firmware_path);
+       }
+       strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin);
+       strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv);
+
+       return 0;
 }
 
 static void pkt_align(struct sk_buff *p, int len, int align)
@@ -2898,16 +2938,13 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 }
 
 #ifdef DEBUG
-static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
-                                  struct sdpcm_shared *sh, char __user *data,
-                                  size_t count)
+static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
+                                  struct sdpcm_shared *sh)
 {
        u32 addr, console_ptr, console_size, console_index;
        char *conbuf = NULL;
        __le32 sh_val;
        int rv;
-       loff_t pos = 0;
-       int nbytes = 0;
 
        /* obtain console information from device memory */
        addr = sh->console_addr + offsetof(struct rte_console, log_le);
@@ -2945,33 +2982,24 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
        if (rv < 0)
                goto done;
 
-       rv = simple_read_from_buffer(data, count, &pos,
-                                    conbuf + console_index,
-                                    console_size - console_index);
+       rv = seq_write(seq, conbuf + console_index,
+                      console_size - console_index);
        if (rv < 0)
                goto done;
 
-       nbytes = rv;
-       if (console_index > 0) {
-               pos = 0;
-               rv = simple_read_from_buffer(data+nbytes, count, &pos,
-                                            conbuf, console_index - 1);
-               if (rv < 0)
-                       goto done;
-               rv += nbytes;
-       }
+       if (console_index > 0)
+               rv = seq_write(seq, conbuf, console_index - 1);
+
 done:
        vfree(conbuf);
        return rv;
 }
 
-static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
-                               char __user *data, size_t count)
+static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
+                               struct sdpcm_shared *sh)
 {
-       int error, res;
-       char buf[350];
+       int error;
        struct brcmf_trap_info tr;
-       loff_t pos = 0;
 
        if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
                brcmf_dbg(INFO, "no trap in firmware\n");
@@ -2983,34 +3011,30 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
        if (error < 0)
                return error;
 
-       res = scnprintf(buf, sizeof(buf),
-                       "dongle trap info: type 0x%x @ epc 0x%08x\n"
-                       "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
-                       "  lr   0x%08x pc   0x%08x offset 0x%x\n"
-                       "  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n"
-                       "  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n",
-                       le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
-                       le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
-                       le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
-                       le32_to_cpu(tr.pc), sh->trap_addr,
-                       le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
-                       le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
-                       le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
-                       le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
-
-       return simple_read_from_buffer(data, count, &pos, buf, res);
+       seq_printf(seq,
+                  "dongle trap info: type 0x%x @ epc 0x%08x\n"
+                  "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+                  "  lr   0x%08x pc   0x%08x offset 0x%x\n"
+                  "  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n"
+                  "  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n",
+                  le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+                  le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+                  le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+                  le32_to_cpu(tr.pc), sh->trap_addr,
+                  le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+                  le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+                  le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+                  le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+       return 0;
 }
 
-static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
-                                 struct sdpcm_shared *sh, char __user *data,
-                                 size_t count)
+static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
+                                 struct sdpcm_shared *sh)
 {
        int error = 0;
-       char buf[200];
        char file[80] = "?";
        char expr[80] = "<???>";
-       int res;
-       loff_t pos = 0;
 
        if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
                brcmf_dbg(INFO, "firmware not built with -assert\n");
@@ -3035,10 +3059,9 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
        }
        sdio_release_host(bus->sdiodev->func[1]);
 
-       res = scnprintf(buf, sizeof(buf),
-                       "dongle assert: %s:%d: assert(%s)\n",
-                       file, sh->assert_line, expr);
-       return simple_read_from_buffer(data, count, &pos, buf, res);
+       seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
+                  file, sh->assert_line, expr);
+       return 0;
 }
 
 static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
@@ -3062,59 +3085,75 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
        return 0;
 }
 
-static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data,
-                               size_t count, loff_t *ppos)
+static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
 {
        int error = 0;
        struct sdpcm_shared sh;
-       int nbytes = 0;
-       loff_t pos = *ppos;
-
-       if (pos != 0)
-               return 0;
 
        error = brcmf_sdio_readshared(bus, &sh);
        if (error < 0)
                goto done;
 
-       error = brcmf_sdio_assert_info(bus, &sh, data, count);
+       error = brcmf_sdio_assert_info(seq, bus, &sh);
        if (error < 0)
                goto done;
-       nbytes = error;
 
-       error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count);
+       error = brcmf_sdio_trap_info(seq, bus, &sh);
        if (error < 0)
                goto done;
-       nbytes += error;
 
-       error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count);
-       if (error < 0)
-               goto done;
-       nbytes += error;
+       error = brcmf_sdio_dump_console(seq, bus, &sh);
 
-       error = nbytes;
-       *ppos += nbytes;
 done:
        return error;
 }
 
-static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
-                                       size_t count, loff_t *ppos)
+static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
 {
-       struct brcmf_sdio *bus = f->private_data;
-       int res;
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
 
-       res = brcmf_sdio_died_dump(bus, data, count, ppos);
-       if (res > 0)
-               *ppos += res;
-       return (ssize_t)res;
+       return brcmf_sdio_died_dump(seq, bus);
 }
 
-static const struct file_operations brcmf_sdio_forensic_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_sdio_forensic_read
-};
+static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
+
+       seq_printf(seq,
+                  "intrcount:    %u\nlastintrs:    %u\n"
+                  "pollcnt:      %u\nregfails:     %u\n"
+                  "tx_sderrs:    %u\nfcqueued:     %u\n"
+                  "rxrtx:        %u\nrx_toolong:   %u\n"
+                  "rxc_errors:   %u\nrx_hdrfail:   %u\n"
+                  "rx_badhdr:    %u\nrx_badseq:    %u\n"
+                  "fc_rcvd:      %u\nfc_xoff:      %u\n"
+                  "fc_xon:       %u\nrxglomfail:   %u\n"
+                  "rxglomframes: %u\nrxglompkts:   %u\n"
+                  "f2rxhdrs:     %u\nf2rxdata:     %u\n"
+                  "f2txdata:     %u\nf1regdata:    %u\n"
+                  "tickcnt:      %u\ntx_ctlerrs:   %lu\n"
+                  "tx_ctlpkts:   %lu\nrx_ctlerrs:   %lu\n"
+                  "rx_ctlpkts:   %lu\nrx_readahead: %lu\n",
+                  sdcnt->intrcount, sdcnt->lastintrs,
+                  sdcnt->pollcnt, sdcnt->regfails,
+                  sdcnt->tx_sderrs, sdcnt->fcqueued,
+                  sdcnt->rxrtx, sdcnt->rx_toolong,
+                  sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+                  sdcnt->rx_badhdr, sdcnt->rx_badseq,
+                  sdcnt->fc_rcvd, sdcnt->fc_xoff,
+                  sdcnt->fc_xon, sdcnt->rxglomfail,
+                  sdcnt->rxglomframes, sdcnt->rxglompkts,
+                  sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+                  sdcnt->f2txdata, sdcnt->f1regdata,
+                  sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+                  sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+                  sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+       return 0;
+}
 
 static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
 {
@@ -3124,9 +3163,9 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
        if (IS_ERR_OR_NULL(dentry))
                return;
 
-       debugfs_create_file("forensics", S_IRUGO, dentry, bus,
-                           &brcmf_sdio_forensic_ops);
-       brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+       brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+       brcmf_debugfs_add_entry(drvr, "counters",
+                               brcmf_debugfs_sdio_count_read);
        debugfs_create_u32("console_interval", 0644, dentry,
                           &bus->console_interval);
 }
@@ -3598,17 +3637,17 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                return;
 
        switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
-       case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
                str_tab = sdiod_drvstr_tab1_1v8;
                str_mask = 0x00003800;
                str_shift = 11;
                break;
-       case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
                str_tab = sdiod_drvstr_tab6_1v8;
                str_mask = 0x00001800;
                str_shift = 11;
                break;
-       case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
                /* note: 43143 does not support tristate */
                i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
                if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
@@ -3619,7 +3658,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                        brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
                                  ci->name, drivestrength);
                break;
-       case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
                str_tab = sdiod_drive_strength_tab5_1v8;
                str_mask = 0x00003800;
                str_shift = 11;
@@ -3720,12 +3759,12 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
        u32 val, rev;
 
        val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
-       if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+       if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID &&
            addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
                rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
                if (rev >= 2) {
                        val &= ~CID_ID_MASK;
-                       val |= BCM4339_CHIP_ID;
+                       val |= BRCM_CC_4339_CHIP_ID;
                }
        }
        return val;
@@ -4127,11 +4166,12 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        brcmf_sdio_debugfs_create(bus);
        brcmf_dbg(INFO, "completed!!\n");
 
+       ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
+       if (ret)
+               goto fail;
+
        ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
-                                    brcmf_sdio_get_fwname(bus->ci,
-                                                          BRCMF_FIRMWARE_BIN),
-                                    brcmf_sdio_get_fwname(bus->ci,
-                                                          BRCMF_FIRMWARE_NVRAM),
+                                    sdiodev->fw_name, sdiodev->nvram_name,
                                     brcmf_sdio_firmware_callback);
        if (ret != 0) {
                brcmf_err("async firmware request failed: %d\n", ret);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
new file mode 100644 (file)
index 0000000..50877e3
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+
+#include <brcm_hw_ids.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_dbg.h"
+#include "fwil.h"
+#include "feature.h"
+
+/*
+ * firmware error code received if iovar is unsupported.
+ */
+#define EBRCMF_FEAT_UNSUPPORTED                23
+
+/*
+ * expand feature list to array of feature strings.
+ */
+#define BRCMF_FEAT_DEF(_f) \
+       #_f,
+static const char *brcmf_feat_names[] = {
+       BRCMF_FEAT_LIST
+};
+#undef BRCMF_FEAT_DEF
+
+#ifdef DEBUG
+/*
+ * expand quirk list to array of quirk strings.
+ */
+#define BRCMF_QUIRK_DEF(_q) \
+       #_q,
+static const char * const brcmf_quirk_names[] = {
+       BRCMF_QUIRK_LIST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_debugfs_read() - expose feature info to debugfs.
+ *
+ * @seq: sequence for debugfs entry.
+ * @data: raw data pointer.
+ */
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       u32 feats = bus_if->drvr->feat_flags;
+       u32 quirks = bus_if->drvr->chip_quirks;
+       int id;
+
+       seq_printf(seq, "Features: %08x\n", feats);
+       for (id = 0; id < BRCMF_FEAT_LAST; id++)
+               if (feats & BIT(id))
+                       seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
+       seq_printf(seq, "\nQuirks:   %08x\n", quirks);
+       for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
+               if (quirks & BIT(id))
+                       seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
+       return 0;
+}
+#else
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+       return 0;
+}
+#endif /* DEBUG */
+
+/**
+ * brcmf_feat_iovar_int_get() - determine feature through iovar query.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
+                                    enum brcmf_feat_id id, char *name)
+{
+       u32 data;
+       int err;
+
+       err = brcmf_fil_iovar_int_get(ifp, name, &data);
+       if (err == 0) {
+               brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+               ifp->drvr->feat_flags |= BIT(id);
+       } else {
+               brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+                         brcmf_feat_names[id], err);
+       }
+}
+
+void brcmf_feat_attach(struct brcmf_pub *drvr)
+{
+       struct brcmf_if *ifp = drvr->iflist[0];
+
+       brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
+
+       /* set chip related quirks */
+       switch (drvr->bus_if->chip) {
+       case BRCM_CC_43236_CHIP_ID:
+               drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
+               break;
+       case BRCM_CC_4329_CHIP_ID:
+               drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
+               break;
+       default:
+               /* no quirks */
+               break;
+       }
+
+       brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
+}
+
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
+{
+       return (ifp->drvr->feat_flags & BIT(id));
+}
+
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+                                enum brcmf_feat_quirk quirk)
+{
+       return (ifp->drvr->chip_quirks & BIT(quirk));
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
new file mode 100644 (file)
index 0000000..961d175
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCMF_FEATURE_H
+#define _BRCMF_FEATURE_H
+
+/*
+ * Features:
+ *
+ * MCHAN: multi-channel for concurrent P2P.
+ */
+#define BRCMF_FEAT_LIST \
+       BRCMF_FEAT_DEF(MCHAN)
+/*
+ * Quirks:
+ *
+ * AUTO_AUTH: workaround needed for automatic authentication type.
+ * NEED_MPC: driver needs to disable MPC during scanning operation.
+ */
+#define BRCMF_QUIRK_LIST \
+       BRCMF_QUIRK_DEF(AUTO_AUTH) \
+       BRCMF_QUIRK_DEF(NEED_MPC)
+
+#define BRCMF_FEAT_DEF(_f) \
+       BRCMF_FEAT_ ## _f,
+/*
+ * expand feature list to enumeration.
+ */
+enum brcmf_feat_id {
+       BRCMF_FEAT_LIST
+       BRCMF_FEAT_LAST
+};
+#undef BRCMF_FEAT_DEF
+
+#define BRCMF_QUIRK_DEF(_q) \
+       BRCMF_FEAT_QUIRK_ ## _q,
+/*
+ * expand quirk list to enumeration.
+ */
+enum brcmf_feat_quirk {
+       BRCMF_QUIRK_LIST
+       BRCMF_FEAT_QUIRK_LAST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_attach() - determine features and quirks.
+ *
+ * @drvr: driver instance.
+ */
+void brcmf_feat_attach(struct brcmf_pub *drvr);
+
+/**
+ * brcmf_feat_is_enabled() - query feature.
+ *
+ * @ifp: interface instance.
+ * @id: feature id to check.
+ *
+ * Return: true is feature is enabled; otherwise false.
+ */
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
+
+/**
+ * brcmf_feat_is_quirk_enabled() - query chip quirk.
+ *
+ * @ifp: interface instance.
+ * @quirk: quirk id to check.
+ *
+ * Return: true is quirk is enabled; otherwise false.
+ */
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+                                enum brcmf_feat_quirk quirk);
+
+#endif /* _BRCMF_FEATURE_H */
index 7b7d237c1ddb274ff663c24653a87b2727a89cdd..8ea9f283d2b8065f4766978c2e43e05e6576fba6 100644 (file)
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
+#include <linux/module.h>
 
 #include "dhd_dbg.h"
 #include "firmware.h"
 
+char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
+module_param_string(firmware_path, brcmf_firmware_path,
+                   BRCMF_FW_PATH_LEN, 0440);
+
 enum nvram_parser_state {
        IDLE,
        KEY,
index 6431bfd7afffc6c544fa1e292b3a6a98cfd4a664..4d3482356b77b0e068e1dc095b8888a7c8477094 100644 (file)
 #define BRCMF_FW_REQ_FLAGS             0x00F0
 #define  BRCMF_FW_REQ_NV_OPTIONAL      0x0010
 
+#define        BRCMF_FW_PATH_LEN       256
+#define        BRCMF_FW_NAME_LEN       32
+
+extern char brcmf_firmware_path[];
+
 void brcmf_fw_nvram_free(void *nvram);
 /*
  * Request firmware(s) asynchronously. When the asynchronous request
index 699908de314a94ff3f382f62536756b3f4a5eed2..d42f7d04b65f115a6a370f2015015b6b03718b2a 100644 (file)
@@ -454,6 +454,34 @@ struct brcmf_fws_macdesc_table {
        struct brcmf_fws_mac_descriptor other;
 };
 
+struct brcmf_fws_stats {
+       u32 tlv_parse_failed;
+       u32 tlv_invalid_type;
+       u32 header_only_pkt;
+       u32 header_pulls;
+       u32 pkt2bus;
+       u32 send_pkts[5];
+       u32 requested_sent[5];
+       u32 generic_error;
+       u32 mac_update_failed;
+       u32 mac_ps_update_failed;
+       u32 if_update_failed;
+       u32 packet_request_failed;
+       u32 credit_request_failed;
+       u32 rollback_success;
+       u32 rollback_failed;
+       u32 delayq_full_error;
+       u32 supprq_full_error;
+       u32 txs_indicate;
+       u32 txs_discard;
+       u32 txs_supp_core;
+       u32 txs_supp_ps;
+       u32 txs_tossed;
+       u32 txs_host_tossed;
+       u32 bus_flow_block;
+       u32 fws_flow_block;
+};
+
 struct brcmf_fws_info {
        struct brcmf_pub *drvr;
        spinlock_t spinlock;
@@ -2017,6 +2045,75 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
        brcmf_fws_unlock(fws);
 }
 
+#ifdef DEBUG
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats;
+
+       seq_printf(seq,
+                  "header_pulls:      %u\n"
+                  "header_only_pkt:   %u\n"
+                  "tlv_parse_failed:  %u\n"
+                  "tlv_invalid_type:  %u\n"
+                  "mac_update_fails:  %u\n"
+                  "ps_update_fails:   %u\n"
+                  "if_update_fails:   %u\n"
+                  "pkt2bus:           %u\n"
+                  "generic_error:     %u\n"
+                  "rollback_success:  %u\n"
+                  "rollback_failed:   %u\n"
+                  "delayq_full:       %u\n"
+                  "supprq_full:       %u\n"
+                  "txs_indicate:      %u\n"
+                  "txs_discard:       %u\n"
+                  "txs_suppr_core:    %u\n"
+                  "txs_suppr_ps:      %u\n"
+                  "txs_tossed:        %u\n"
+                  "txs_host_tossed:   %u\n"
+                  "bus_flow_block:    %u\n"
+                  "fws_flow_block:    %u\n"
+                  "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
+                  "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                  fwstats->header_pulls,
+                  fwstats->header_only_pkt,
+                  fwstats->tlv_parse_failed,
+                  fwstats->tlv_invalid_type,
+                  fwstats->mac_update_failed,
+                  fwstats->mac_ps_update_failed,
+                  fwstats->if_update_failed,
+                  fwstats->pkt2bus,
+                  fwstats->generic_error,
+                  fwstats->rollback_success,
+                  fwstats->rollback_failed,
+                  fwstats->delayq_full_error,
+                  fwstats->supprq_full_error,
+                  fwstats->txs_indicate,
+                  fwstats->txs_discard,
+                  fwstats->txs_supp_core,
+                  fwstats->txs_supp_ps,
+                  fwstats->txs_tossed,
+                  fwstats->txs_host_tossed,
+                  fwstats->bus_flow_block,
+                  fwstats->fws_flow_block,
+                  fwstats->send_pkts[0], fwstats->send_pkts[1],
+                  fwstats->send_pkts[2], fwstats->send_pkts[3],
+                  fwstats->send_pkts[4],
+                  fwstats->requested_sent[0],
+                  fwstats->requested_sent[1],
+                  fwstats->requested_sent[2],
+                  fwstats->requested_sent[3],
+                  fwstats->requested_sent[4]);
+
+       return 0;
+}
+#else
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+       return 0;
+}
+#endif
+
 int brcmf_fws_init(struct brcmf_pub *drvr)
 {
        struct brcmf_fws_info *fws;
@@ -2107,7 +2204,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
-       brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
+       brcmf_debugfs_add_entry(drvr, "fws_stats",
+                               brcmf_debugfs_fws_stats_read);
 
        brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
                  fws->fw_signals ? "enabled" : "disabled", tlv);
index 3deab7959a0d9a5c3b22ad757caf4615b283b596..6c5e585ccda99fcf83cf8d8ae5e7a7b22130e542 100644 (file)
@@ -18,6 +18,8 @@
 #define        _BRCM_SDH_H_
 
 #include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include "firmware.h"
 
 #define SDIO_FUNC_0            0
 #define SDIO_FUNC_1            1
@@ -182,6 +184,8 @@ struct brcmf_sdio_dev {
        uint max_segment_size;
        uint txglomsz;
        struct sg_table sgtable;
+       char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+       char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
 };
 
 /* sdio core registers */
index 839bcda9465a8b5bc2a630558c6378b5cf07f33d..dc135915470d2cb70efab5480d11a036a0e00d3f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/vmalloc.h>
 
 #include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
 #include <brcmu_wifi.h>
 #include <dhd_bus.h>
 #include <dhd_dbg.h>
@@ -913,16 +914,16 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
 static bool brcmf_usb_chip_support(int chipid, int chiprev)
 {
        switch(chipid) {
-       case 43143:
+       case BRCM_CC_43143_CHIP_ID:
                return true;
-       case 43235:
-       case 43236:
-       case 43238:
+       case BRCM_CC_43235_CHIP_ID:
+       case BRCM_CC_43236_CHIP_ID:
+       case BRCM_CC_43238_CHIP_ID:
                return (chiprev == 3);
-       case 43242:
+       case BRCM_CC_43242_CHIP_ID:
                return true;
-       case 43566:
-       case 43569:
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
                return true;
        default:
                break;
@@ -1016,16 +1017,16 @@ static int check_file(const u8 *headers)
 static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
 {
        switch (devinfo->bus_pub.devid) {
-       case 43143:
+       case BRCM_CC_43143_CHIP_ID:
                return BRCMF_USB_43143_FW_NAME;
-       case 43235:
-       case 43236:
-       case 43238:
+       case BRCM_CC_43235_CHIP_ID:
+       case BRCM_CC_43236_CHIP_ID:
+       case BRCM_CC_43238_CHIP_ID:
                return BRCMF_USB_43236_FW_NAME;
-       case 43242:
+       case BRCM_CC_43242_CHIP_ID:
                return BRCMF_USB_43242_FW_NAME;
-       case 43566:
-       case 43569:
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
                return BRCMF_USB_43569_FW_NAME;
        default:
                return NULL;
@@ -1183,8 +1184,6 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
        bus->bus_priv.usb = bus_pub;
        dev_set_drvdata(dev, bus);
        bus->ops = &brcmf_usb_bus_ops;
-       bus->chip = bus_pub->devid;
-       bus->chiprev = bus_pub->chiprev;
        bus->proto_type = BRCMF_PROTO_BCDC;
        bus->always_use_fws_queue = true;
 
@@ -1193,6 +1192,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
                if (ret)
                        goto fail;
        }
+       bus->chip = bus_pub->devid;
+       bus->chiprev = bus_pub->chiprev;
+
        /* request firmware here */
        brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL,
                               brcmf_usb_probe_phase2);
@@ -1365,21 +1367,17 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
                                      brcmf_usb_probe_phase2);
 }
 
-#define BRCMF_USB_VENDOR_ID_BROADCOM   0x0a5c
-#define BRCMF_USB_DEVICE_ID_43143      0xbd1e
-#define BRCMF_USB_DEVICE_ID_43236      0xbd17
-#define BRCMF_USB_DEVICE_ID_43242      0xbd1f
-#define BRCMF_USB_DEVICE_ID_43569      0xbd27
-#define BRCMF_USB_DEVICE_ID_BCMFW      0x0bdc
+#define BRCMF_USB_DEVICE(dev_id)       \
+       { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
 
 static struct usb_device_id brcmf_usb_devid_table[] = {
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) },
+       BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
        /* special entry for device with firmware loaded and running */
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
-       { }
+       BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+       { /* end: all zeroes */ }
 };
 
 MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
index 9682cf213ec46394c14d759d84ffdfc4794eea3c..48078a321716787069c7491880c0e783316acb3a 100644 (file)
@@ -33,6 +33,7 @@
 #include "p2p.h"
 #include "btcoex.h"
 #include "wl_cfg80211.h"
+#include "feature.h"
 #include "fwil.h"
 #include "vendor.h"
 
@@ -102,24 +103,6 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
        return true;
 }
 
-#define CHAN2G(_channel, _freq, _flags) {                      \
-       .band                   = IEEE80211_BAND_2GHZ,          \
-       .center_freq            = (_freq),                      \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
-#define CHAN5G(_channel, _flags) {                             \
-       .band                   = IEEE80211_BAND_5GHZ,          \
-       .center_freq            = 5000 + (5 * (_channel)),      \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
 #define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
 #define RATETAB_ENT(_rateid, _flags) \
        {                                                               \
@@ -148,58 +131,17 @@ static struct ieee80211_rate __wl_rates[] = {
 #define wl_g_rates             (__wl_rates + 0)
 #define wl_g_rates_size        12
 
-static struct ieee80211_channel __wl_2ghz_channels[] = {
-       CHAN2G(1, 2412, 0),
-       CHAN2G(2, 2417, 0),
-       CHAN2G(3, 2422, 0),
-       CHAN2G(4, 2427, 0),
-       CHAN2G(5, 2432, 0),
-       CHAN2G(6, 2437, 0),
-       CHAN2G(7, 2442, 0),
-       CHAN2G(8, 2447, 0),
-       CHAN2G(9, 2452, 0),
-       CHAN2G(10, 2457, 0),
-       CHAN2G(11, 2462, 0),
-       CHAN2G(12, 2467, 0),
-       CHAN2G(13, 2472, 0),
-       CHAN2G(14, 2484, 0),
-};
-
-static struct ieee80211_channel __wl_5ghz_a_channels[] = {
-       CHAN5G(34, 0), CHAN5G(36, 0),
-       CHAN5G(38, 0), CHAN5G(40, 0),
-       CHAN5G(42, 0), CHAN5G(44, 0),
-       CHAN5G(46, 0), CHAN5G(48, 0),
-       CHAN5G(52, 0), CHAN5G(56, 0),
-       CHAN5G(60, 0), CHAN5G(64, 0),
-       CHAN5G(100, 0), CHAN5G(104, 0),
-       CHAN5G(108, 0), CHAN5G(112, 0),
-       CHAN5G(116, 0), CHAN5G(120, 0),
-       CHAN5G(124, 0), CHAN5G(128, 0),
-       CHAN5G(132, 0), CHAN5G(136, 0),
-       CHAN5G(140, 0), CHAN5G(149, 0),
-       CHAN5G(153, 0), CHAN5G(157, 0),
-       CHAN5G(161, 0), CHAN5G(165, 0),
-       CHAN5G(184, 0), CHAN5G(188, 0),
-       CHAN5G(192, 0), CHAN5G(196, 0),
-       CHAN5G(200, 0), CHAN5G(204, 0),
-       CHAN5G(208, 0), CHAN5G(212, 0),
-       CHAN5G(216, 0),
-};
-
-static struct ieee80211_supported_band __wl_band_2ghz = {
+/* Band templates duplicated per wiphy. The channel info
+ * is filled in after querying the device.
+ */
+static const struct ieee80211_supported_band __wl_band_2ghz = {
        .band = IEEE80211_BAND_2GHZ,
-       .channels = __wl_2ghz_channels,
-       .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
        .bitrates = wl_g_rates,
        .n_bitrates = wl_g_rates_size,
-       .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true},
 };
 
-static struct ieee80211_supported_band __wl_band_5ghz_a = {
+static const struct ieee80211_supported_band __wl_band_5ghz_a = {
        .band = IEEE80211_BAND_5GHZ,
-       .channels = __wl_5ghz_a_channels,
-       .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
        .bitrates = wl_a_rates,
        .n_bitrates = wl_a_rates_size,
 };
@@ -592,7 +534,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
 
 static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
 {
-       if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329)
+       if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
                brcmf_set_mpc(ifp, mpc);
 }
 
@@ -1619,17 +1561,10 @@ static
 enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
                                           enum nl80211_auth_type type)
 {
-       u32 ci;
-       if (type == NL80211_AUTHTYPE_AUTOMATIC) {
-               /* shift to ignore chip revision */
-               ci = brcmf_get_chip_info(ifp) >> 4;
-               switch (ci) {
-               case 43236:
-                       brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
-                       return NL80211_AUTHTYPE_OPEN_SYSTEM;
-               default:
-                       break;
-               }
+       if (type == NL80211_AUTHTYPE_AUTOMATIC &&
+           brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
+               brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
+               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
        }
        return type;
 }
@@ -4284,122 +4219,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .tdls_oper = brcmf_cfg80211_tdls_oper,
 };
 
-static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
-{
-       /* scheduled scan settings */
-       wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
-       wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
-       wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
-       wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-}
-
-static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
-       {
-               .max = 2,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                        BIT(NL80211_IFTYPE_ADHOC) |
-                        BIT(NL80211_IFTYPE_AP)
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                        BIT(NL80211_IFTYPE_P2P_GO)
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
-       }
-};
-static const struct ieee80211_iface_combination brcmf_iface_combos[] = {
-       {
-                .max_interfaces = BRCMF_IFACE_MAX_CNT,
-                .num_different_channels = 2,
-                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
-                .limits = brcmf_iface_limits
-       }
-};
-
-static const struct ieee80211_txrx_stypes
-brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
-       [NL80211_IFTYPE_STATION] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       },
-       [NL80211_IFTYPE_P2P_CLIENT] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       },
-       [NL80211_IFTYPE_P2P_GO] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
-                     BIT(IEEE80211_STYPE_AUTH >> 4) |
-                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
-                     BIT(IEEE80211_STYPE_ACTION >> 4)
-       },
-       [NL80211_IFTYPE_P2P_DEVICE] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       }
-};
-
-static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
-{
-       struct wiphy *wiphy;
-       s32 err = 0;
-
-       wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
-       if (!wiphy) {
-               brcmf_err("Could not allocate wiphy device\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       set_wiphy_dev(wiphy, phydev);
-       wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
-       wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
-       wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
-       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_ADHOC) |
-                                BIT(NL80211_IFTYPE_AP) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                BIT(NL80211_IFTYPE_P2P_GO) |
-                                BIT(NL80211_IFTYPE_P2P_DEVICE);
-       wiphy->iface_combinations = brcmf_iface_combos;
-       wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
-       wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
-       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-       wiphy->cipher_suites = __wl_cipher_suites;
-       wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
-       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
-                       WIPHY_FLAG_OFFCHAN_TX |
-                       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                       WIPHY_FLAG_SUPPORTS_TDLS;
-       if (!brcmf_roamoff)
-               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
-       wiphy->mgmt_stypes = brcmf_txrx_stypes;
-       wiphy->max_remain_on_channel_duration = 5000;
-       brcmf_wiphy_pno_params(wiphy);
-       brcmf_dbg(INFO, "Registering custom regulatory\n");
-       wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
-       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
-       /* vendor commands/events support */
-       wiphy->vendor_commands = brcmf_vendor_cmds;
-       wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
-
-       err = wiphy_register(wiphy);
-       if (err < 0) {
-               brcmf_err("Could not register wiphy device (%d)\n", err);
-               wiphy_free(wiphy);
-               return ERR_PTR(err);
-       }
-       return wiphy;
-}
-
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
                                           enum nl80211_iftype type,
                                           bool pm_block)
@@ -4943,138 +4762,6 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
        mutex_init(&event->vif_event_lock);
 }
 
-static int brcmf_enable_bw40_2g(struct brcmf_if *ifp)
-{
-       struct brcmf_fil_bwcap_le band_bwcap;
-       u32 val;
-       int err;
-
-       /* verify support for bw_cap command */
-       val = WLC_BAND_5G;
-       err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
-
-       if (!err) {
-               /* only set 2G bandwidth using bw_cap command */
-               band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
-               band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
-               err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
-                                              sizeof(band_bwcap));
-       } else {
-               brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
-               val = WLC_N_BW_40ALL;
-               err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
-       }
-       return err;
-}
-
-struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
-                                                 struct device *busdev)
-{
-       struct net_device *ndev = drvr->iflist[0]->ndev;
-       struct brcmf_cfg80211_info *cfg;
-       struct wiphy *wiphy;
-       struct brcmf_cfg80211_vif *vif;
-       struct brcmf_if *ifp;
-       s32 err = 0;
-       s32 io_type;
-
-       if (!ndev) {
-               brcmf_err("ndev is invalid\n");
-               return NULL;
-       }
-
-       ifp = netdev_priv(ndev);
-       wiphy = brcmf_setup_wiphy(busdev);
-       if (IS_ERR(wiphy))
-               return NULL;
-
-       cfg = wiphy_priv(wiphy);
-       cfg->wiphy = wiphy;
-       cfg->pub = drvr;
-       init_vif_event(&cfg->vif_event);
-       INIT_LIST_HEAD(&cfg->vif_list);
-
-       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
-       if (IS_ERR(vif)) {
-               wiphy_free(wiphy);
-               return NULL;
-       }
-
-       vif->ifp = ifp;
-       vif->wdev.netdev = ndev;
-       ndev->ieee80211_ptr = &vif->wdev;
-       SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
-
-       err = wl_init_priv(cfg);
-       if (err) {
-               brcmf_err("Failed to init iwm_priv (%d)\n", err);
-               goto cfg80211_attach_out;
-       }
-       ifp->vif = vif;
-
-       err = brcmf_p2p_attach(cfg);
-       if (err) {
-               brcmf_err("P2P initilisation failed (%d)\n", err);
-               goto cfg80211_p2p_attach_out;
-       }
-       err = brcmf_btcoex_attach(cfg);
-       if (err) {
-               brcmf_err("BT-coex initialisation failed (%d)\n", err);
-               brcmf_p2p_detach(&cfg->p2p);
-               goto cfg80211_p2p_attach_out;
-       }
-
-       /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
-        * setup 40MHz in 2GHz band and enable OBSS scanning.
-        */
-       if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap &
-           IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
-               err = brcmf_enable_bw40_2g(ifp);
-               if (!err)
-                       err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
-                                                     BRCMF_OBSS_COEX_AUTO);
-       }
-       /* clear for now and rely on update later */
-       wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false;
-       wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0;
-
-       err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
-       if (err) {
-               brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
-               wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
-       }
-
-       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
-                                   &io_type);
-       if (err) {
-               brcmf_err("Failed to get D11 version (%d)\n", err);
-               goto cfg80211_p2p_attach_out;
-       }
-       cfg->d11inf.io_type = (u8)io_type;
-       brcmu_d11_attach(&cfg->d11inf);
-
-       return cfg;
-
-cfg80211_p2p_attach_out:
-       wl_deinit_priv(cfg);
-
-cfg80211_attach_out:
-       brcmf_free_vif(vif);
-       return NULL;
-}
-
-void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
-{
-       if (!cfg)
-               return;
-
-       WARN_ON(!list_empty(&cfg->vif_list));
-       wiphy_unregister(cfg->wiphy);
-       brcmf_btcoex_detach(cfg);
-       wl_deinit_priv(cfg);
-       wiphy_free(cfg->wiphy);
-}
-
 static s32
 brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
 {
@@ -5167,42 +4854,128 @@ dongle_scantime_out:
        return err;
 }
 
-
-static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
-                                  u32 bw_cap[])
+/* Filter the list of channels received from firmware counting only
+ * the 20MHz channels. The wiphy band data only needs those which get
+ * flagged to indicate if they can take part in higher bandwidth.
+ */
+static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
+                                      struct brcmf_chanspec_list *chlist,
+                                      u32 chcnt[])
 {
-       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       struct ieee80211_channel *band_chan_arr;
-       struct brcmf_chanspec_list *list;
+       u32 total = le32_to_cpu(chlist->count);
        struct brcmu_chan ch;
-       s32 err;
-       u8 *pbuf;
-       u32 i, j;
-       u32 total;
-       enum ieee80211_band band;
-       u32 channel;
-       u32 *n_cnt;
-       u32 index;
-       u32 ht40_flag;
-       bool update;
-       u32 array_size;
+       int i;
 
-       pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+       for (i = 0; i <= total; i++) {
+               ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
+               cfg->d11inf.decchspec(&ch);
 
-       if (pbuf == NULL)
-               return -ENOMEM;
+               /* Firmware gives a ordered list. We skip non-20MHz
+                * channels is 2G. For 5G we can abort upon reaching
+                * a non-20MHz channel in the list.
+                */
+               if (ch.bw != BRCMU_CHAN_BW_20) {
+                       if (ch.band == BRCMU_CHAN_BAND_5G)
+                               break;
+                       else
+                               continue;
+               }
 
-       list = (struct brcmf_chanspec_list *)pbuf;
+               if (ch.band == BRCMU_CHAN_BAND_2G)
+                       chcnt[0] += 1;
+               else if (ch.band == BRCMU_CHAN_BAND_5G)
+                       chcnt[1] += 1;
+       }
+}
 
-       err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
-                                      BRCMF_DCMD_MEDLEN);
+static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+                                          struct brcmu_chan *ch)
+{
+       u32 ht40_flag;
+
+       ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
+       if (ch->sb == BRCMU_CHAN_SB_U) {
+               if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                       channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+               channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+       } else {
+               /* It should be one of
+                * IEEE80211_CHAN_NO_HT40 or
+                * IEEE80211_CHAN_NO_HT40PLUS
+                */
+               channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+               if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                       channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+       }
+}
+
+static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+                                   u32 bw_cap[])
+{
+       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+       struct ieee80211_supported_band *band;
+       struct ieee80211_channel *channel;
+       struct wiphy *wiphy;
+       struct brcmf_chanspec_list *list;
+       struct brcmu_chan ch;
+       int err;
+       u8 *pbuf;
+       u32 i, j;
+       u32 total;
+       u32 chaninfo;
+       u32 chcnt[2] = { 0, 0 };
+       u32 index;
+
+       pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+       if (pbuf == NULL)
+               return -ENOMEM;
+
+       list = (struct brcmf_chanspec_list *)pbuf;
+
+       err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+                                      BRCMF_DCMD_MEDLEN);
        if (err) {
                brcmf_err("get chanspecs error (%d)\n", err);
-               goto exit;
+               goto fail_pbuf;
        }
 
-       __wl_band_2ghz.n_channels = 0;
-       __wl_band_5ghz_a.n_channels = 0;
+       brcmf_count_20mhz_channels(cfg, list, chcnt);
+       wiphy = cfg_to_wiphy(cfg);
+       if (chcnt[0]) {
+               band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+                              GFP_KERNEL);
+               if (band == NULL) {
+                       err = -ENOMEM;
+                       goto fail_pbuf;
+               }
+               band->channels = kcalloc(chcnt[0], sizeof(*channel),
+                                        GFP_KERNEL);
+               if (band->channels == NULL) {
+                       kfree(band);
+                       err = -ENOMEM;
+                       goto fail_pbuf;
+               }
+               band->n_channels = 0;
+               wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+       }
+       if (chcnt[1]) {
+               band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
+                              GFP_KERNEL);
+               if (band == NULL) {
+                       err = -ENOMEM;
+                       goto fail_band2g;
+               }
+               band->channels = kcalloc(chcnt[1], sizeof(*channel),
+                                        GFP_KERNEL);
+               if (band->channels == NULL) {
+                       kfree(band);
+                       err = -ENOMEM;
+                       goto fail_band2g;
+               }
+               band->n_channels = 0;
+               wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+       }
 
        total = le32_to_cpu(list->count);
        for (i = 0; i < total; i++) {
@@ -5210,100 +4983,151 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                cfg->d11inf.decchspec(&ch);
 
                if (ch.band == BRCMU_CHAN_BAND_2G) {
-                       band_chan_arr = __wl_2ghz_channels;
-                       array_size = ARRAY_SIZE(__wl_2ghz_channels);
-                       n_cnt = &__wl_band_2ghz.n_channels;
-                       band = IEEE80211_BAND_2GHZ;
+                       band = wiphy->bands[IEEE80211_BAND_2GHZ];
                } else if (ch.band == BRCMU_CHAN_BAND_5G) {
-                       band_chan_arr = __wl_5ghz_a_channels;
-                       array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
-                       n_cnt = &__wl_band_5ghz_a.n_channels;
-                       band = IEEE80211_BAND_5GHZ;
+                       band = wiphy->bands[IEEE80211_BAND_5GHZ];
                } else {
                        brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
                        continue;
                }
-               if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
+               if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_40)
                        continue;
-               if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
+               if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_80)
                        continue;
-               update = false;
-               for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
-                       if (band_chan_arr[j].hw_value == ch.chnum) {
-                               update = true;
+
+               channel = band->channels;
+               index = band->n_channels;
+               for (j = 0; j < band->n_channels; j++) {
+                       if (channel[j].hw_value == ch.chnum) {
+                               index = j;
                                break;
                        }
                }
-               if (update)
-                       index = j;
-               else
-                       index = *n_cnt;
-               if (index <  array_size) {
-                       band_chan_arr[index].center_freq =
-                               ieee80211_channel_to_frequency(ch.chnum, band);
-                       band_chan_arr[index].hw_value = ch.chnum;
-
-                       /* assuming the chanspecs order is HT20,
-                        * HT40 upper, HT40 lower, and VHT80.
+               channel[index].center_freq =
+                       ieee80211_channel_to_frequency(ch.chnum, band->band);
+               channel[index].hw_value = ch.chnum;
+
+               /* assuming the chanspecs order is HT20,
+                * HT40 upper, HT40 lower, and VHT80.
+                */
+               if (ch.bw == BRCMU_CHAN_BW_80) {
+                       channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
+               } else if (ch.bw == BRCMU_CHAN_BW_40) {
+                       brcmf_update_bw40_channel_flag(&channel[index], &ch);
+               } else {
+                       /* disable other bandwidths for now as mentioned
+                        * order assure they are enabled for subsequent
+                        * chanspecs.
                         */
-                       if (ch.bw == BRCMU_CHAN_BW_80) {
-                               band_chan_arr[index].flags &=
-                                       ~IEEE80211_CHAN_NO_80MHZ;
-                       } else if (ch.bw == BRCMU_CHAN_BW_40) {
-                               ht40_flag = band_chan_arr[index].flags &
-                                           IEEE80211_CHAN_NO_HT40;
-                               if (ch.sb == BRCMU_CHAN_SB_U) {
-                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
-                                               band_chan_arr[index].flags &=
-                                                       ~IEEE80211_CHAN_NO_HT40;
-                                       band_chan_arr[index].flags |=
-                                               IEEE80211_CHAN_NO_HT40PLUS;
-                               } else {
-                                       /* It should be one of
-                                        * IEEE80211_CHAN_NO_HT40 or
-                                        * IEEE80211_CHAN_NO_HT40PLUS
-                                        */
-                                       band_chan_arr[index].flags &=
-                                                       ~IEEE80211_CHAN_NO_HT40;
-                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
-                                               band_chan_arr[index].flags |=
-                                                   IEEE80211_CHAN_NO_HT40MINUS;
-                               }
-                       } else {
-                               /* disable other bandwidths for now as mentioned
-                                * order assure they are enabled for subsequent
-                                * chanspecs.
-                                */
-                               band_chan_arr[index].flags =
-                                               IEEE80211_CHAN_NO_HT40 |
-                                               IEEE80211_CHAN_NO_80MHZ;
-                               ch.bw = BRCMU_CHAN_BW_20;
-                               cfg->d11inf.encchspec(&ch);
-                               channel = ch.chspec;
-                               err = brcmf_fil_bsscfg_int_get(ifp,
-                                                              "per_chan_info",
-                                                              &channel);
-                               if (!err) {
-                                       if (channel & WL_CHAN_RADAR)
-                                               band_chan_arr[index].flags |=
-                                                       (IEEE80211_CHAN_RADAR |
-                                                       IEEE80211_CHAN_NO_IR);
-                                       if (channel & WL_CHAN_PASSIVE)
-                                               band_chan_arr[index].flags |=
-                                                   IEEE80211_CHAN_NO_IR;
-                               }
+                       channel[index].flags = IEEE80211_CHAN_NO_HT40 |
+                                              IEEE80211_CHAN_NO_80MHZ;
+                       ch.bw = BRCMU_CHAN_BW_20;
+                       cfg->d11inf.encchspec(&ch);
+                       chaninfo = ch.chspec;
+                       err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
+                                                      &chaninfo);
+                       if (!err) {
+                               if (chaninfo & WL_CHAN_RADAR)
+                                       channel[index].flags |=
+                                               (IEEE80211_CHAN_RADAR |
+                                                IEEE80211_CHAN_NO_IR);
+                               if (chaninfo & WL_CHAN_PASSIVE)
+                                       channel[index].flags |=
+                                               IEEE80211_CHAN_NO_IR;
                        }
-                       if (!update)
-                               (*n_cnt)++;
                }
+               if (index == band->n_channels)
+                       band->n_channels++;
        }
-exit:
+       kfree(pbuf);
+       return 0;
+
+fail_band2g:
+       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+       wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+fail_pbuf:
        kfree(pbuf);
        return err;
 }
 
+static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+{
+       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+       struct ieee80211_supported_band *band;
+       struct brcmf_fil_bwcap_le band_bwcap;
+       struct brcmf_chanspec_list *list;
+       u8 *pbuf;
+       u32 val;
+       int err;
+       struct brcmu_chan ch;
+       u32 num_chan;
+       int i, j;
+
+       /* verify support for bw_cap command */
+       val = WLC_BAND_5G;
+       err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+       if (!err) {
+               /* only set 2G bandwidth using bw_cap command */
+               band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+               band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
+               err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+                                              sizeof(band_bwcap));
+       } else {
+               brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+               val = WLC_N_BW_40ALL;
+               err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+       }
+
+       if (!err) {
+               /* update channel info in 2G band */
+               pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+               if (pbuf == NULL)
+                       return -ENOMEM;
+
+               ch.band = BRCMU_CHAN_BAND_2G;
+               ch.bw = BRCMU_CHAN_BW_40;
+               ch.chnum = 0;
+               cfg->d11inf.encchspec(&ch);
+
+               /* pass encoded chanspec in query */
+               *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
+
+               err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+                                              BRCMF_DCMD_MEDLEN);
+               if (err) {
+                       brcmf_err("get chanspecs error (%d)\n", err);
+                       kfree(pbuf);
+                       return err;
+               }
+
+               band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
+               list = (struct brcmf_chanspec_list *)pbuf;
+               num_chan = le32_to_cpu(list->count);
+               for (i = 0; i < num_chan; i++) {
+                       ch.chspec = (u16)le32_to_cpu(list->element[i]);
+                       cfg->d11inf.decchspec(&ch);
+                       if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
+                               continue;
+                       if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
+                               continue;
+                       for (j = 0; j < band->n_channels; j++) {
+                               if (band->channels[j].hw_value == ch.chnum)
+                                       break;
+                       }
+                       if (WARN_ON(j == band->n_channels))
+                               continue;
+
+                       brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
+               }
+       }
+       return err;
+}
+
 static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
 {
        u32 band, mimo_bwcap;
@@ -5394,44 +5218,19 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
        band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
 }
 
-static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
+static int brcmf_setup_wiphybands(struct wiphy *wiphy)
 {
+       struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       struct wiphy *wiphy;
-       s32 phy_list;
-       u32 band_list[3];
        u32 nmode = 0;
        u32 vhtmode = 0;
-       u32 bw_cap[2] = { 0, 0 };
+       u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
        u32 rxchain;
        u32 nchain;
-       s8 phy;
-       s32 err;
-       u32 nband;
+       int err;
        s32 i;
-       struct ieee80211_supported_band *bands[2] = { NULL, NULL };
        struct ieee80211_supported_band *band;
 
-       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
-                                    &phy_list, sizeof(phy_list));
-       if (err) {
-               brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
-               return err;
-       }
-
-       phy = ((char *)&phy_list)[0];
-       brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
-
-
-       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
-                                    &band_list, sizeof(band_list));
-       if (err) {
-               brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
-               return err;
-       }
-       brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
-                 band_list[0], band_list[1], band_list[2]);
-
        (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
        err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
        if (err) {
@@ -5453,44 +5252,129 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        }
        brcmf_dbg(INFO, "nchain=%d\n", nchain);
 
-       err = brcmf_construct_reginfo(cfg, bw_cap);
+       err = brcmf_construct_chaninfo(cfg, bw_cap);
        if (err) {
-               brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
+               brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
                return err;
        }
 
-       nband = band_list[0];
-
-       for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
-               band = NULL;
-               if ((band_list[i] == WLC_BAND_5G) &&
-                   (__wl_band_5ghz_a.n_channels > 0))
-                       band = &__wl_band_5ghz_a;
-               else if ((band_list[i] == WLC_BAND_2G) &&
-                        (__wl_band_2ghz.n_channels > 0))
-                       band = &__wl_band_2ghz;
-               else
+       wiphy = cfg_to_wiphy(cfg);
+       for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
+               band = wiphy->bands[i];
+               if (band == NULL)
                        continue;
 
                if (nmode)
                        brcmf_update_ht_cap(band, bw_cap, nchain);
                if (vhtmode)
                        brcmf_update_vht_cap(band, bw_cap, nchain);
-               bands[band->band] = band;
        }
 
-       wiphy = cfg_to_wiphy(cfg);
-       wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
-       wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
-       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
-       return err;
+       return 0;
 }
 
+static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+       {
+               .max = 2,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                        BIT(NL80211_IFTYPE_ADHOC) |
+                        BIT(NL80211_IFTYPE_AP)
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                        BIT(NL80211_IFTYPE_P2P_GO)
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+       }
+};
+static struct ieee80211_iface_combination brcmf_iface_combos[] = {
+       {
+                .max_interfaces = BRCMF_IFACE_MAX_CNT,
+                .num_different_channels = 1,
+                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
+                .limits = brcmf_iface_limits
+       }
+};
 
-static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                     BIT(IEEE80211_STYPE_AUTH >> 4) |
+                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                     BIT(IEEE80211_STYPE_ACTION >> 4)
+       },
+       [NL80211_IFTYPE_P2P_DEVICE] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       }
+};
+
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
 {
-       return brcmf_update_wiphybands(cfg);
+       /* scheduled scan settings */
+       wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+       wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+       wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+       wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+}
+
+static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+       struct ieee80211_iface_combination ifc_combo;
+       wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+       wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+       wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_ADHOC) |
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                BIT(NL80211_IFTYPE_P2P_GO) |
+                                BIT(NL80211_IFTYPE_P2P_DEVICE);
+       /* need VSDB firmware feature for concurrent channels */
+       ifc_combo = brcmf_iface_combos[0];
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+               ifc_combo.num_different_channels = 2;
+       wiphy->iface_combinations = kmemdup(&ifc_combo,
+                                           sizeof(ifc_combo),
+                                           GFP_KERNEL);
+       wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+       wiphy->cipher_suites = __wl_cipher_suites;
+       wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+                       WIPHY_FLAG_OFFCHAN_TX |
+                       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                       WIPHY_FLAG_SUPPORTS_TDLS;
+       if (!brcmf_roamoff)
+               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+       wiphy->mgmt_stypes = brcmf_txrx_stypes;
+       wiphy->max_remain_on_channel_duration = 5000;
+       brcmf_wiphy_pno_params(wiphy);
+
+       /* vendor commands/events support */
+       wiphy->vendor_commands = brcmf_vendor_cmds;
+       wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
+       return brcmf_setup_wiphybands(wiphy);
 }
 
 static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
@@ -5528,9 +5412,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
                                          NULL, NULL);
        if (err)
                goto default_conf_out;
-       err = brcmf_dongle_probecap(cfg);
-       if (err)
-               goto default_conf_out;
 
        brcmf_configure_arp_offload(ifp, true);
 
@@ -5658,3 +5539,150 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
                                  vif_event_equals(event, action), timeout);
 }
 
+static void brcmf_free_wiphy(struct wiphy *wiphy)
+{
+       kfree(wiphy->iface_combinations);
+       if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+               kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+               kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+       }
+       if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
+               kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
+               kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
+       }
+       wiphy_free(wiphy);
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+                                                 struct device *busdev)
+{
+       struct net_device *ndev = drvr->iflist[0]->ndev;
+       struct brcmf_cfg80211_info *cfg;
+       struct wiphy *wiphy;
+       struct brcmf_cfg80211_vif *vif;
+       struct brcmf_if *ifp;
+       s32 err = 0;
+       s32 io_type;
+       u16 *cap = NULL;
+
+       if (!ndev) {
+               brcmf_err("ndev is invalid\n");
+               return NULL;
+       }
+
+       ifp = netdev_priv(ndev);
+       wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+       if (!wiphy) {
+               brcmf_err("Could not allocate wiphy device\n");
+               return NULL;
+       }
+       set_wiphy_dev(wiphy, busdev);
+
+       cfg = wiphy_priv(wiphy);
+       cfg->wiphy = wiphy;
+       cfg->pub = drvr;
+       init_vif_event(&cfg->vif_event);
+       INIT_LIST_HEAD(&cfg->vif_list);
+
+       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
+       if (IS_ERR(vif))
+               goto wiphy_out;
+
+       vif->ifp = ifp;
+       vif->wdev.netdev = ndev;
+       ndev->ieee80211_ptr = &vif->wdev;
+       SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
+       err = wl_init_priv(cfg);
+       if (err) {
+               brcmf_err("Failed to init iwm_priv (%d)\n", err);
+               brcmf_free_vif(vif);
+               goto wiphy_out;
+       }
+       ifp->vif = vif;
+
+       /* determine d11 io type before wiphy setup */
+       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
+       if (err) {
+               brcmf_err("Failed to get D11 version (%d)\n", err);
+               goto priv_out;
+       }
+       cfg->d11inf.io_type = (u8)io_type;
+       brcmu_d11_attach(&cfg->d11inf);
+
+       err = brcmf_setup_wiphy(wiphy, ifp);
+       if (err < 0)
+               goto priv_out;
+
+       brcmf_dbg(INFO, "Registering custom regulatory\n");
+       wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+       /* firmware defaults to 40MHz disabled in 2G band. We signal
+        * cfg80211 here that we do and have it decide we can enable
+        * it. But first check if device does support 2G operation.
+        */
+       if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+               cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
+               *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       err = wiphy_register(wiphy);
+       if (err < 0) {
+               brcmf_err("Could not register wiphy device (%d)\n", err);
+               goto priv_out;
+       }
+
+       /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+        * setup 40MHz in 2GHz band and enable OBSS scanning.
+        */
+       if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+               err = brcmf_enable_bw40_2g(cfg);
+               if (!err)
+                       err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+                                                     BRCMF_OBSS_COEX_AUTO);
+               else
+                       *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+
+       err = brcmf_p2p_attach(cfg);
+       if (err) {
+               brcmf_err("P2P initilisation failed (%d)\n", err);
+               goto wiphy_unreg_out;
+       }
+       err = brcmf_btcoex_attach(cfg);
+       if (err) {
+               brcmf_err("BT-coex initialisation failed (%d)\n", err);
+               brcmf_p2p_detach(&cfg->p2p);
+               goto wiphy_unreg_out;
+       }
+
+       err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+       if (err) {
+               brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+               wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+       }
+
+       return cfg;
+
+wiphy_unreg_out:
+       wiphy_unregister(cfg->wiphy);
+priv_out:
+       wl_deinit_priv(cfg);
+       brcmf_free_vif(vif);
+wiphy_out:
+       brcmf_free_wiphy(wiphy);
+       return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
+{
+       if (!cfg)
+               return;
+
+       WARN_ON(!list_empty(&cfg->vif_list));
+       wiphy_unregister(cfg->wiphy);
+       brcmf_btcoex_detach(cfg);
+       brcmf_p2p_detach(&cfg->p2p);
+       wl_deinit_priv(cfg);
+       brcmf_free_wiphy(cfg->wiphy);
+}
index af8ba64ace39286e3214d69bfc23f776e66b6336..1b474828d5b884fb957daf52ac59ed3c135b4b1c 100644 (file)
@@ -4707,41 +4707,6 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,
        return err;
 }
 
-static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc)
-{
-       uint unit;
-       unit = wlc->pub->unit;
-
-       if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
-               /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
-               wlc->band->antgain = 8;
-       } else if (wlc->band->antgain == -1) {
-               wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
-                         " srom, using 2dB\n", unit, __func__);
-               wlc->band->antgain = 8;
-       } else {
-               s8 gain, fract;
-               /* Older sroms specified gain in whole dbm only.  In order
-                * be able to specify qdbm granularity and remain backward
-                * compatible the whole dbms are now encoded in only
-                * low 6 bits and remaining qdbms are encoded in the hi 2 bits.
-                * 6 bit signed number ranges from -32 - 31.
-                *
-                * Examples:
-                * 0x1 = 1 db,
-                * 0xc1 = 1.75 db (1 + 3 quarters),
-                * 0x3f = -1 (-1 + 0 quarters),
-                * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm.
-                * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm.
-                */
-               gain = wlc->band->antgain & 0x3f;
-               gain <<= 2;     /* Sign extend */
-               gain >>= 2;
-               fract = (wlc->band->antgain & 0xc0) >> 6;
-               wlc->band->antgain = 4 * gain + fract;
-       }
-}
-
 static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
 {
        int aa;
@@ -4780,8 +4745,6 @@ static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
        else
                wlc->band->antgain = sprom->antenna_gain.a0;
 
-       brcms_c_attach_antgain_init(wlc);
-
        return true;
 }
 
index d816270db3be56cd7af0ec130fc6bf74b9aa0b69..64d1a7ba040ccc430dc65e7f5f3b662dbcd388f0 100644 (file)
 #ifndef        _BRCM_HW_IDS_H_
 #define        _BRCM_HW_IDS_H_
 
-#define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
+#include <linux/pci_ids.h>
+#include <linux/mmc/sdio_ids.h>
+
+#define BRCM_USB_VENDOR_ID_BROADCOM    0x0a5c
+#define BRCM_PCIE_VENDOR_ID_BROADCOM   PCI_VENDOR_ID_BROADCOM
+#define BRCM_SDIO_VENDOR_ID_BROADCOM   SDIO_VENDOR_ID_BROADCOM
+
+/* Chipcommon Core Chip IDs */
+#define BRCM_CC_43143_CHIP_ID          43143
+#define BRCM_CC_43235_CHIP_ID          43235
+#define BRCM_CC_43236_CHIP_ID          43236
+#define BRCM_CC_43238_CHIP_ID          43238
+#define BRCM_CC_43241_CHIP_ID          0x4324
+#define BRCM_CC_43242_CHIP_ID          43242
+#define BRCM_CC_4329_CHIP_ID           0x4329
+#define BRCM_CC_4330_CHIP_ID           0x4330
+#define BRCM_CC_4334_CHIP_ID           0x4334
+#define BRCM_CC_43362_CHIP_ID          43362
+#define BRCM_CC_4335_CHIP_ID           0x4335
+#define BRCM_CC_4339_CHIP_ID           0x4339
+#define BRCM_CC_4354_CHIP_ID           0x4354
+#define BRCM_CC_43566_CHIP_ID          43566
+#define BRCM_CC_43569_CHIP_ID          43569
+
+/* SDIO Device IDs */
+#define BRCM_SDIO_43143_DEVICE_ID      BRCM_CC_43143_CHIP_ID
+#define BRCM_SDIO_43241_DEVICE_ID      BRCM_CC_43241_CHIP_ID
+#define BRCM_SDIO_4329_DEVICE_ID       BRCM_CC_4329_CHIP_ID
+#define BRCM_SDIO_4330_DEVICE_ID       BRCM_CC_4330_CHIP_ID
+#define BRCM_SDIO_4334_DEVICE_ID       BRCM_CC_4334_CHIP_ID
+#define BRCM_SDIO_43362_DEVICE_ID      BRCM_CC_43362_CHIP_ID
+#define BRCM_SDIO_4335_4339_DEVICE_ID  BRCM_CC_4335_CHIP_ID
+#define BRCM_SDIO_4354_DEVICE_ID       BRCM_CC_4354_CHIP_ID
 
+/* USB Device IDs */
+#define BRCM_USB_43143_DEVICE_ID       0xbd1e
+#define BRCM_USB_43236_DEVICE_ID       0xbd17
+#define BRCM_USB_43242_DEVICE_ID       0xbd1f
+#define BRCM_USB_43569_DEVICE_ID       0xbd27
+#define BRCM_USB_BCMFW_DEVICE_ID       0x0bdc
+
+/* brcmsmac IDs */
+#define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
 #define BCM43224_D11N_ID       0x4353  /* 43224 802.11n dualband device */
 #define BCM43224_D11N_ID_VEN1  0x0576  /* Vendor specific 43224 802.11n db */
-
 #define BCM43225_D11N2G_ID     0x4357  /* 43225 802.11n 2.4GHz device */
-
 #define BCM43236_D11N_ID       0x4346  /* 43236 802.11n dualband device */
 #define BCM43236_D11N2G_ID     0x4347  /* 43236 802.11n 2.4GHz device */
 
-/* Chipcommon Core Chip IDs */
 #define BCM4313_CHIP_ID                0x4313
-#define BCM43143_CHIP_ID       43143
 #define BCM43224_CHIP_ID       43224
-#define BCM43225_CHIP_ID       43225
-#define BCM43235_CHIP_ID       43235
-#define BCM43236_CHIP_ID       43236
-#define BCM43238_CHIP_ID       43238
-#define BCM43241_CHIP_ID       0x4324
-#define BCM4329_CHIP_ID                0x4329
-#define BCM4330_CHIP_ID                0x4330
-#define BCM4331_CHIP_ID                0x4331
-#define BCM4334_CHIP_ID                0x4334
-#define BCM4335_CHIP_ID                0x4335
-#define BCM43362_CHIP_ID       43362
-#define BCM4339_CHIP_ID                0x4339
-#define BCM4354_CHIP_ID                0x4354
 
 #endif                         /* _BRCM_HW_IDS_H_ */
index ed50de6362ed1d5dcd56b45243ff0b140dcbafe5..6dc5dd3ced44723943934f114c6fde78c98a565f 100644 (file)
@@ -1068,13 +1068,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
        /* recalculate basic rates */
        iwl_calc_basic_rates(priv, ctx);
 
-       /*
-        * force CTS-to-self frames protection if RTS-CTS is not preferred
-        * one aggregation protection method
-        */
-       if (!priv->hw_params.use_rts_for_aggregation)
-               ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
-
        if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
            !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
                ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
@@ -1480,11 +1473,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
        else
                ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
 
-       if (bss_conf->use_cts_prot)
-               ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
-       else
-               ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
-
        memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
 
        if (vif->type == NL80211_IFTYPE_AP ||
index bb842f4732dd8a76a374b228ce2b5d3c5066d09a..77e3178040b2a80716ff97cb3c4771b356682897 100644 (file)
@@ -1407,5 +1407,5 @@ MODULE_PARM_DESC(power_level,
                 "default power save level (range from 1 - 5, default: 1)");
 
 module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
-MODULE_PARM_DESC(dbgm,
+MODULE_PARM_DESC(fw_monitor,
                 "firmware monitor - to debug FW (default: false - needs lots of memory)");
index ced5ba95c23d82c1212fb37cbb042faf79323591..c39a0b899e83aef7b780786b01f90e29039d9259 100644 (file)
@@ -92,8 +92,8 @@ enum iwl_fw_error_dump_type {
 /**
  * struct iwl_fw_error_dump_data - data for one type
  * @type: %enum iwl_fw_error_dump_type
- * @len: the length starting from %data - must be a multiplier of 4.
- * @data: the data itself padded to be a multiplier of 4.
+ * @len: the length starting from %data
+ * @data: the data itself
  */
 struct iwl_fw_error_dump_data {
        __le32 type;
@@ -147,14 +147,14 @@ struct iwl_fw_error_dump_info {
 } __packed;
 
 /**
- * struct iwl_fw_error_fw_mon - FW monitor data
+ * struct iwl_fw_error_dump_fw_mon - FW monitor data
  * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
  * @fw_mon_base_ptr: base pointer of the data
  * @fw_mon_cycle_cnt: number of wrap arounds
  * @reserved: for future use
  * @data: captured data
  */
-struct iwl_fw_error_fw_mon {
+struct iwl_fw_error_dump_fw_mon {
        __le32 fw_mon_wr_ptr;
        __le32 fw_mon_base_ptr;
        __le32 fw_mon_cycle_cnt;
index 13e5d69845e9e7653b7010a53197a121c1d02e64..1bb5193c5b1b5097c11bbf5a986f4c4d6e409c58 100644 (file)
@@ -90,6 +90,7 @@
  *     P2P client interfaces simultaneously if they are in different bindings.
  * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
  *     P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
  * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
  * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
  * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
@@ -120,14 +121,18 @@ enum iwl_ucode_tlv_flag {
  * enum iwl_ucode_tlv_api - ucode api
  * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
  * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
  * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
  * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
  */
 enum iwl_ucode_tlv_api {
        IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
        IWL_UCODE_TLV_CAPA_EXTENDED_BEACON      = BIT(1),
+       IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
        IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
        IWL_UCODE_TLV_API_DISABLE_STA_TX        = BIT(5),
+       IWL_UCODE_TLV_API_LMAC_SCAN             = BIT(6),
 };
 
 /**
index f0ae038f333960bc129c4be68c1f3b9bb82d0a5e..018af2957d3b7b319c47222eae4d1dfe1cdc3891 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/etherdevice.h>
+#include <linux/pci.h>
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
@@ -87,8 +88,10 @@ enum wkp_nvm_offsets {
 
 enum family_8000_nvm_offsets {
        /* NVM HW-Section offset (in words) definitions */
-       HW_ADDR0_FAMILY_8000 = 0x12,
-       HW_ADDR1_FAMILY_8000 = 0x16,
+       HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
+       HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
+       HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
+       HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
        MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
 
        /* NVM SW-Section offset (in words) definitions */
@@ -504,16 +507,52 @@ static void iwl_set_hw_address_family_8000(struct device *dev,
        }
 
        if (nvm_hw) {
-               /* take the MAC address from the OTP */
-               hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
-               data->hw_addr[0] = hw_addr[3];
-               data->hw_addr[1] = hw_addr[2];
-               data->hw_addr[2] = hw_addr[1];
-               data->hw_addr[3] = hw_addr[0];
-
-               hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
-               data->hw_addr[4] = hw_addr[1];
-               data->hw_addr[5] = hw_addr[0];
+               /* read the MAC address from OTP */
+               if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
+                       /* read the mac address from the WFPM location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_WFPM_FAMILY_8000);
+                       data->hw_addr[0] = hw_addr[3];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[2] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[0];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_WFPM_FAMILY_8000);
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[5] = hw_addr[0];
+               } else if ((data->nvm_version >= 0xE08) &&
+                          (data->nvm_version < 0xE0B)) {
+                       /* read "reverse order"  from the PCIe location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_PCIE_FAMILY_8000);
+                       data->hw_addr[5] = hw_addr[2];
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[0];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_PCIE_FAMILY_8000);
+                       data->hw_addr[2] = hw_addr[3];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[0] = hw_addr[1];
+               } else {
+                       /* read from the PCIe location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_PCIE_FAMILY_8000);
+                       data->hw_addr[5] = hw_addr[0];
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[2];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_PCIE_FAMILY_8000);
+                       data->hw_addr[2] = hw_addr[1];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[0] = hw_addr[3];
+               }
+               if (!is_valid_ether_addr(data->hw_addr))
+                       IWL_ERR_DEV(dev,
+                                   "mac address from hw section is not valid\n");
+
                return;
        }
 
index c30d7f64ec1e4e1c47a635e091c4e503c2ceaa83..a28235913c2cf94c5b07b9c0e3dc3c8768a7d990 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 sf.o
 iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o coex.o
+iwlmvm-y += power.o coex.o coex_legacy.o
 iwlmvm-y += tt.o offloading.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
index 7e0388a32912b4ab60de5cbbb1a315fc2f8c1f5c..8110fe00bf5512635e5a050655c4d5ea7d011d68 100644 (file)
 #include "mvm.h"
 #include "iwl-debug.h"
 
-#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant)                 \
-       [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) |    \
-                  ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
-
-static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
-                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
-                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
-                      BT_COEX_PRIO_TBL_PRIO_LOW, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
-                      BT_COEX_PRIO_TBL_PRIO_LOW, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
-                      BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
-                      BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
-                      BT_COEX_PRIO_TBL_DISABLED, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
-       0, 0, 0, 0, 0, 0,
-};
-
-#undef EVENT_PRIO_ANT
-
 #define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
-static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
-{
-       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
-               return 0;
-
-       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
-                                   sizeof(struct iwl_bt_coex_prio_tbl_cmd),
-                                   &iwl_bt_prio_tbl);
-}
-
 const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
        [BT_KILL_MSK_DEFAULT] = 0xffff0000,
        [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
@@ -520,6 +480,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum iwl_bt_coex_lut_type ret;
        u16 phy_ctx_id;
+       u32 primary_ch_phy_id, secondary_ch_phy_id;
 
        /*
         * Checking that we hold mvm->mutex is a good idea, but the rate
@@ -547,10 +508,13 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
        }
 
        phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+       primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
+       secondary_ch_phy_id =
+               le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
 
-       if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
+       if (primary_ch_phy_id == phy_ctx_id)
                ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
-       else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
+       else if (secondary_ch_phy_id == phy_ctx_id)
                ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
        /* else - default = TX TX disallowed */
 
@@ -568,11 +532,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        int ret;
-       u32 flags;
+       u32 mode;
 
-       ret = iwl_send_bt_prio_tbl(mvm);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_send_bt_init_conf_old(mvm);
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
@@ -582,68 +545,50 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        lockdep_assert_held(&mvm->mutex);
 
        if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+               u32 mode;
+
                switch (mvm->bt_force_ant_mode) {
-               case BT_FORCE_ANT_AUTO:
-                       flags = BT_COEX_AUTO;
-                       break;
                case BT_FORCE_ANT_BT:
-                       flags = BT_COEX_BT;
+                       mode = BT_COEX_BT;
                        break;
                case BT_FORCE_ANT_WIFI:
-                       flags = BT_COEX_WIFI;
+                       mode = BT_COEX_WIFI;
                        break;
                default:
                        WARN_ON(1);
-                       flags = 0;
+                       mode = 0;
                }
 
-               bt_cmd->flags = cpu_to_le32(flags);
-               bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
+               bt_cmd->mode = cpu_to_le32(mode);
                goto send_cmd;
        }
 
-       bt_cmd->max_kill = 5;
-       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
-       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
-       bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
-       bt_cmd->bt4_tx_rx_max_freq0 = 15;
-       bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
-       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
-
-       flags = iwlwifi_mod_params.bt_coex_active ?
-                       BT_COEX_NW : BT_COEX_DISABLE;
-       bt_cmd->flags = cpu_to_le32(flags);
-
-       bt_cmd->valid_bit_msk = cpu_to_le32(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_VALID_WIFI_RX_SW_PRIO_BOOST |
-                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
-                                           BT_VALID_ANT_ISOLATION |
-                                           BT_VALID_ANT_ISOLATION_THRS |
-                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
-                                           BT_VALID_TXRX_MAX_FREQ_0 |
-                                           BT_VALID_SYNC_TO_SCO);
+       bt_cmd->max_kill = cpu_to_le32(5);
+       bt_cmd->bt4_antenna_isolation_thr =
+                               cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD);
+       bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
+       bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
+       bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+       bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+
+       mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
+       bt_cmd->mode = cpu_to_le32(mode);
 
        if (IWL_MVM_BT_COEX_SYNC2SCO)
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+               bt_cmd->enabled_modules |=
+                       cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
 
-       if (IWL_MVM_BT_COEX_CORUNNING) {
-               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
-                                                    BT_VALID_CORUN_LUT_40);
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
-       }
+       if (IWL_MVM_BT_COEX_CORUNNING)
+               bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
 
        if (IWL_MVM_BT_COEX_MPLUT) {
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
-               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+               bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
+               bt_cmd->enabled_modules |=
+                       cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
        }
 
+       bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
+
        if (mvm->cfg->bt_shared_single_ant)
                memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
                       sizeof(iwl_single_shared_ant));
@@ -651,20 +596,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
                       sizeof(iwl_combined_lookup));
 
-       /* Take first Co-running block LUT to get started */
-       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
-              sizeof(bt_cmd->bt4_corun_lut20));
-       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
-              sizeof(bt_cmd->bt4_corun_lut40));
-
-       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+       memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
               sizeof(iwl_bt_prio_boost));
-       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+       memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut,
               sizeof(iwl_bt_mprio_lut));
-       bt_cmd->kill_ack_msk =
-               cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
-       bt_cmd->kill_cts_msk =
-               cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
 send_cmd:
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
@@ -676,19 +611,12 @@ send_cmd:
        return ret;
 }
 
-static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
-                                          bool reduced_tx_power)
+static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm,
+                                     bool reduced_tx_power)
 {
        enum iwl_bt_kill_msk bt_kill_msk;
-       struct iwl_bt_coex_cmd *bt_cmd;
+       struct iwl_bt_coex_sw_boost_update_cmd 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, },
-       };
-       int ret = 0;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -718,40 +646,30 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
 
        mvm->bt_kill_msk = bt_kill_msk;
 
-       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
-       if (!bt_cmd)
-               return -ENOMEM;
-       cmd.data[0] = bt_cmd;
-       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
+       cmd.boost_values[0].kill_ack_msk =
+               cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+       cmd.boost_values[0].kill_cts_msk =
+               cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
 
-       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_le32(BT_VALID_ENABLE |
-                                            BT_VALID_KILL_ACK |
-                                            BT_VALID_KILL_CTS);
+       cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
+       cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
+       cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
+       cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
 
        IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
                       iwl_bt_ack_kill_msk[bt_kill_msk],
                       iwl_bt_cts_kill_msk[bt_kill_msk]);
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
-
-       kfree(bt_cmd);
-       return ret;
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
+                                   sizeof(cmd), &cmd);
 }
 
 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
                                       bool enable)
 {
-       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_NOCOPY, },
-               .flags = CMD_ASYNC,
-       };
+       struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
        struct iwl_mvm_sta *mvmsta;
+       u32 value;
        int ret;
 
        mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
@@ -762,27 +680,20 @@ 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->flags = cpu_to_le32(BT_COEX_NW);
-
-       bt_cmd->valid_bit_msk =
-               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
-       bt_cmd->bt_reduced_tx_power = sta_id;
+       value = mvmsta->sta_id;
 
        if (enable)
-               bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+               value |= BT_REDUCED_TX_POWER_BIT;
 
        IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
                       enable ? "en" : "dis", sta_id);
 
+       cmd.reduced_txp = cpu_to_le32(value);
        mvmsta->bt_reduced_txpower = enable;
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
+                                  sizeof(cmd), &cmd);
 
-       kfree(bt_cmd);
        return ret;
 }
 
@@ -876,10 +787,13 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        if (!vif->bss_conf.assoc)
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+                              mvmvif->phy_ctxt->id))
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        IWL_DEBUG_COEX(data->mvm,
-                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
-                      mvmvif->id, data->notif->bt_status, bt_activity_grading,
-                      smps_mode);
+                      "mac %d: bt_activity_grading %d smps_req %d\n",
+                      mvmvif->id, bt_activity_grading, smps_mode);
 
        iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
 
@@ -931,7 +845,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
         */
        if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
            mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
-           !data->notif->bt_status) {
+           le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
                data->reduced_tx_power = false;
                iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
@@ -998,9 +912,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                if (chan->def.width < NL80211_CHAN_WIDTH_40) {
                        ci_bw_idx = 0;
-                       cmd.co_run_bw_primary = 0;
                } else {
-                       cmd.co_run_bw_primary = 1;
                        if (chan->def.center_freq1 >
                            chan->def.chan->center_freq)
                                ci_bw_idx = 2;
@@ -1010,7 +922,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                cmd.bt_primary_ci =
                        iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
-               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+               cmd.primary_ch_phy_id =
+                       cpu_to_le32(*((u16 *)data.primary->drv_priv));
        }
 
        if (data.secondary) {
@@ -1022,9 +935,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                if (chan->def.width < NL80211_CHAN_WIDTH_40) {
                        ci_bw_idx = 0;
-                       cmd.co_run_bw_secondary = 0;
                } else {
-                       cmd.co_run_bw_secondary = 1;
                        if (chan->def.center_freq1 >
                            chan->def.chan->center_freq)
                                ci_bw_idx = 2;
@@ -1034,7 +945,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                cmd.bt_secondary_ci =
                        iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
-               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+               cmd.secondary_ch_phy_id =
+                       cpu_to_le32(*((u16 *)data.secondary->drv_priv));
        }
 
        rcu_read_unlock();
@@ -1054,7 +966,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
         */
        data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
 
-       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1065,11 +977,10 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd);
 
        IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
-       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
-                      notif->bt_status ? "ON" : "OFF");
-       IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
        IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
        IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
                       le32_to_cpu(notif->primary_ch_lut));
@@ -1077,8 +988,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                       le32_to_cpu(notif->secondary_ch_lut));
        IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
                       le32_to_cpu(notif->bt_activity_grading));
-       IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
-                      notif->bt_agg_traffic_load);
 
        /* remember this notification for future use: rssi fluctuations */
        memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@@ -1148,6 +1057,11 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        };
        int ret;
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
+               return;
+       }
+
        lockdep_assert_held(&mvm->mutex);
 
        /* Ignore updates if we are in force mode */
@@ -1162,7 +1076,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return;
 
        /* No BT - reports should be disabled */
-       if (!mvm->last_bt_notif.bt_status)
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
                return;
 
        IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
@@ -1193,7 +1107,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
         */
        data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
 
-       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1204,13 +1118,18 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
        enum iwl_bt_coex_lut_type lut_type;
 
-       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
-           BT_HIGH_TRAFFIC)
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+       if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
                return LINK_QUAL_AGG_TIME_LIMIT_DEF;
 
-       if (mvm->last_bt_notif.ttc_enabled)
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
                return LINK_QUAL_AGG_TIME_LIMIT_DEF;
 
        lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
@@ -1226,9 +1145,14 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
                                     struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
        enum iwl_bt_coex_lut_type lut_type;
 
-       if (mvm->last_bt_notif.ttc_enabled)
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+       if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
                return true;
 
        if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
@@ -1248,6 +1172,9 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
 
 bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
        return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF;
 }
 
@@ -1256,6 +1183,9 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
 {
        u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
+
        if (band != IEEE80211_BAND_2GHZ)
                return false;
 
@@ -1296,6 +1226,11 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 
 void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               iwl_mvm_bt_coex_vif_change_old(mvm);
+               return;
+       }
+
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
 
@@ -1305,16 +1240,12 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+       struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
        u8 __maybe_unused lower_bound, upper_bound;
-       int ret;
        u8 lut;
 
-       struct iwl_bt_coex_cmd *bt_cmd;
-       struct iwl_host_cmd cmd = {
-               .id = BT_CONFIG,
-               .len = { sizeof(*bt_cmd), },
-               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-       };
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
 
        if (!IWL_MVM_BT_COEX_CORUNNING)
                return 0;
@@ -1349,25 +1280,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
 
        mvm->last_corun_lut = lut;
 
-       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
-       if (!bt_cmd)
-               return 0;
-       cmd.data[0] = bt_cmd;
-
-       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
-       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
-                                            BT_VALID_CORUN_LUT_20 |
-                                            BT_VALID_CORUN_LUT_40);
-
        /* For the moment, use the same LUT for 20GHz and 40GHz */
-       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
-              sizeof(bt_cmd->bt4_corun_lut20));
+       memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
+              sizeof(cmd.corun_lut20));
 
-       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
-              sizeof(bt_cmd->bt4_corun_lut40));
+       memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
+              sizeof(cmd.corun_lut40));
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
-
-       kfree(bt_cmd);
-       return ret;
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
+                                   sizeof(cmd), &cmd);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
new file mode 100644 (file)
index 0000000..ce50363
--- /dev/null
@@ -0,0 +1,1332 @@
+/******************************************************************************
+ *
+ * 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 - 2014 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 - 2014 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/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "fw-api-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant)                 \
+       [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) |    \
+                  ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+                      BT_COEX_PRIO_TBL_DISABLED, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+       0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+#define BT_ANTENNA_COUPLING_THRESHOLD          (30)
+
+static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
+                                   sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+                                   &iwl_bt_prio_tbl);
+}
+
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+       cpu_to_le32(0xf0f0f0f0), /* 50% */
+       cpu_to_le32(0xc0c0c0c0), /* 25% */
+       cpu_to_le32(0xfcfcfcfc), /* 75% */
+       cpu_to_le32(0xfefefefe), /* 87.5% */
+};
+
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+};
+
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               /* Tight */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0x00004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Loose */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Tx Tx disabled */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xeeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+};
+
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+       /* dummy entry for channel 0 */
+       {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+       {
+               cpu_to_le64(0x0000001FFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000000FFFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000003FFFCULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+       },
+       {
+               cpu_to_le64(0x00001FFFE0ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+       },
+       {
+               cpu_to_le64(0x00007FFF80ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+       },
+       {
+               cpu_to_le64(0x0003FFFC00ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+       },
+       {
+               cpu_to_le64(0x000FFFF000ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+       },
+       {
+               cpu_to_le64(0x007FFF8000ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+       },
+       {
+               cpu_to_le64(0x01FFFE0000ULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+       },
+       {
+               cpu_to_le64(0x0FFFF00000ULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+               cpu_to_le64(0x0ULL),
+       },
+       {
+               cpu_to_le64(0x3FFFC00000ULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFFE000000ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFF8000000ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFC0000000ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0ULL)
+       },
+};
+
+static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
+       cpu_to_le32(0x28412201),
+       cpu_to_le32(0x11118451),
+};
+
+struct corunning_block_luts {
+       u8 range;
+       __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ *             LUT0: [ 0, 12[
+ *             LUT1: [12, 20[
+ *             LUT2: [20, 21[
+ *             LUT3: [21, 23[
+ *             LUT4: [23, 27[
+ *             LUT5: [27, 30[
+ *             LUT6: [30, 32[
+ *             LUT7: [32, 33[
+ *             LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+       {
+               .range = 0,
+               .lut20 = {
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 12,
+               .lut20 = {
+                       cpu_to_le32(0x00000001),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 20,
+               .lut20 = {
+                       cpu_to_le32(0x00000002),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 21,
+               .lut20 = {
+                       cpu_to_le32(0x00000003),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 23,
+               .lut20 = {
+                       cpu_to_le32(0x00000004),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 27,
+               .lut20 = {
+                       cpu_to_le32(0x00000005),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 30,
+               .lut20 = {
+                       cpu_to_le32(0x00000006),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 32,
+               .lut20 = {
+                       cpu_to_le32(0x00000007),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 33,
+               .lut20 = {
+                       cpu_to_le32(0x00000008),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum iwl_bt_coex_lut_type ret;
+       u16 phy_ctx_id;
+
+       /*
+        * Checking that we hold mvm->mutex is a good idea, but the rate
+        * control can't acquire the mutex since it runs in Tx path.
+        * So this is racy in that case, but in the worst case, the AMPDU
+        * size limit will be wrong for a short time which is not a big
+        * issue.
+        */
+
+       rcu_read_lock();
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return BT_COEX_INVALID_LUT;
+       }
+
+       ret = BT_COEX_TX_DIS_LUT;
+
+       if (mvm->cfg->bt_shared_single_ant) {
+               rcu_read_unlock();
+               return ret;
+       }
+
+       phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+       if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut);
+       else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut);
+       /* else - default = TX TX disallowed */
+
+       rcu_read_unlock();
+
+       return ret;
+}
+
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       int ret;
+       u32 flags;
+
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               return ret;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+               switch (mvm->bt_force_ant_mode) {
+               case BT_FORCE_ANT_AUTO:
+                       flags = BT_COEX_AUTO_OLD;
+                       break;
+               case BT_FORCE_ANT_BT:
+                       flags = BT_COEX_BT_OLD;
+                       break;
+               case BT_FORCE_ANT_WIFI:
+                       flags = BT_COEX_WIFI_OLD;
+                       break;
+               default:
+                       WARN_ON(1);
+                       flags = 0;
+               }
+
+               bt_cmd->flags = cpu_to_le32(flags);
+               bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
+               goto send_cmd;
+       }
+
+       bt_cmd->max_kill = 5;
+       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
+       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
+       bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
+       bt_cmd->bt4_tx_rx_max_freq0 = 15;
+       bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
+       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
+
+       flags = iwlwifi_mod_params.bt_coex_active ?
+                       BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD;
+       bt_cmd->flags = cpu_to_le32(flags);
+
+       bt_cmd->valid_bit_msk = cpu_to_le32(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_VALID_WIFI_RX_SW_PRIO_BOOST |
+                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+                                           BT_VALID_ANT_ISOLATION |
+                                           BT_VALID_ANT_ISOLATION_THRS |
+                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
+                                           BT_VALID_TXRX_MAX_FREQ_0 |
+                                           BT_VALID_SYNC_TO_SCO);
+
+       if (IWL_MVM_BT_COEX_SYNC2SCO)
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+
+       if (IWL_MVM_BT_COEX_CORUNNING) {
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+                                                    BT_VALID_CORUN_LUT_40);
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+       }
+
+       if (IWL_MVM_BT_COEX_MPLUT) {
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+       }
+
+       if (mvm->cfg->bt_shared_single_ant)
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+                      sizeof(iwl_single_shared_ant));
+       else
+               memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+                      sizeof(iwl_combined_lookup));
+
+       /* Take first Co-running block LUT to get started */
+       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
+              sizeof(bt_cmd->bt4_corun_lut20));
+       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
+              sizeof(bt_cmd->bt4_corun_lut40));
+
+       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+              sizeof(iwl_bt_prio_boost));
+       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+              sizeof(iwl_bt_mprio_lut));
+       bt_cmd->kill_ack_msk =
+               cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
+       bt_cmd->kill_cts_msk =
+               cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
+
+send_cmd:
+       memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+       memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+
+       ret = iwl_mvm_send_cmd(mvm, &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_old *bt_cmd;
+       struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .data[0] = &bt_cmd,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       int ret = 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (reduced_tx_power) {
+               /* Reduced Tx power has precedence on the type of the profile */
+               bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
+       } else {
+               /* Low latency BT profile is active: give higher prio to BT */
+               if (BT_MBOX_MSG(notif, 3, SCO_STATE)  ||
+                   BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
+                   BT_MBOX_MSG(notif, 3, SNIFF_STATE))
+                       bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
+               else
+                       bt_kill_msk = BT_KILL_MSK_DEFAULT;
+       }
+
+       IWL_DEBUG_COEX(mvm,
+                      "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
+                      bt_kill_msk,
+                      BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
+                      BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
+                      BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
+
+       /* Don't send HCMD if there is no update */
+       if (bt_kill_msk == mvm->bt_kill_msk)
+               return 0;
+
+       mvm->bt_kill_msk = bt_kill_msk;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+       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_le32(BT_VALID_ENABLE |
+                                            BT_VALID_KILL_ACK |
+                                            BT_VALID_KILL_CTS);
+
+       IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
+                      iwl_bt_ack_kill_msk[bt_kill_msk],
+                      iwl_bt_cts_kill_msk[bt_kill_msk]);
+
+       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_old *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_NOCOPY, },
+               .flags = CMD_ASYNC,
+       };
+       struct iwl_mvm_sta *mvmsta;
+       int ret;
+
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+       if (!mvmsta)
+               return 0;
+
+       /* nothing to do */
+       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->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+       bt_cmd->valid_bit_msk =
+               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
+       bt_cmd->bt_reduced_tx_power = sta_id;
+
+       if (enable)
+               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;
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
+
+struct iwl_bt_iterator_data {
+       struct iwl_bt_coex_profile_notif_old *notif;
+       struct iwl_mvm *mvm;
+       u32 num_bss_ifaces;
+       bool reduced_tx_power;
+       struct ieee80211_chanctx_conf *primary;
+       struct ieee80211_chanctx_conf *secondary;
+       bool primary_ll;
+};
+
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      bool enable, int rssi)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->bf_data.last_bt_coex_event = rssi;
+       mvmvif->bf_data.bt_coex_max_thold =
+               enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
+       mvmvif->bf_data.bt_coex_min_thold =
+               enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
+}
+
+/* must be called under rcu_read_lock */
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_bt_iterator_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum ieee80211_smps_mode smps_mode;
+       u32 bt_activity_grading;
+       int ave_rssi;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /* Count BSSes vifs */
+               data->num_bss_ifaces++;
+               /* default smps_mode for BSS / P2P client is AUTOMATIC */
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+               break;
+       case NL80211_IFTYPE_AP:
+               /* default smps_mode for AP / GO is OFF */
+               smps_mode = IEEE80211_SMPS_OFF;
+               if (!mvmvif->ap_ibss_active) {
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
+                       return;
+               }
+
+               /* the Ack / Cts kill mask must be default if AP / GO */
+               data->reduced_tx_power = false;
+               break;
+       default:
+               return;
+       }
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       /* If channel context is invalid or not on 2.4GHz .. */
+       if ((!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+               /* ... relax constraints and disable rssi events */
+               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                   smps_mode);
+               data->reduced_tx_power = false;
+               if (vif->type == NL80211_IFTYPE_STATION) {
+                       iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                   false);
+                       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               }
+               return;
+       }
+
+       bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+       if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+               smps_mode = IEEE80211_SMPS_STATIC;
+       else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+               smps_mode = vif->type == NL80211_IFTYPE_AP ?
+                               IEEE80211_SMPS_OFF :
+                               IEEE80211_SMPS_DYNAMIC;
+
+       /* relax SMPS contraints for next association */
+       if (!vif->bss_conf.assoc)
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+       IWL_DEBUG_COEX(data->mvm,
+                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
+                      mvmvif->id, data->notif->bt_status, bt_activity_grading,
+                      smps_mode);
+
+       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
+
+       /* low latency is always primary */
+       if (iwl_mvm_vif_low_latency(mvmvif)) {
+               data->primary_ll = true;
+
+               data->secondary = data->primary;
+               data->primary = chanctx_conf;
+       }
+
+       if (vif->type == NL80211_IFTYPE_AP) {
+               if (!mvmvif->ap_ibss_active)
+                       return;
+
+               if (chanctx_conf == data->primary)
+                       return;
+
+               if (!data->primary_ll) {
+                       /*
+                        * downgrade the current primary no matter what its
+                        * type is.
+                        */
+                       data->secondary = data->primary;
+                       data->primary = chanctx_conf;
+               } else {
+                       /* there is low latency vif - we will be secondary */
+                       data->secondary = chanctx_conf;
+               }
+               return;
+       }
+
+       /*
+        * STA / P2P Client, try to be primary if first vif. If we are in low
+        * latency mode, we are already in primary and just don't do much
+        */
+       if (!data->primary || data->primary == chanctx_conf)
+               data->primary = chanctx_conf;
+       else if (!data->secondary)
+               /* if secondary is not NULL, it might be a GO */
+               data->secondary = chanctx_conf;
+
+       /*
+        * don't reduce the Tx power if one of these is true:
+        *  we are in LOOSE
+        *  single share antenna product
+        *  BT is active
+        *  we are associated
+        */
+       if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+           mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+           !data->notif->bt_status) {
+               data->reduced_tx_power = false;
+               iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               return;
+       }
+
+       /* try to get the avg rssi from fw */
+       ave_rssi = mvmvif->bf_data.ave_beacon_signal;
+
+       /* if the RSSI isn't valid, fake it is very low */
+       if (!ave_rssi)
+               ave_rssi = -100;
+       if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
+               if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+                       IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+               /*
+                * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
+                * BSS / P2P clients have rssi above threshold.
+                * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
+                * the iteration, if one interface's rssi isn't good enough,
+                * bt_kill_msk will be set to default values.
+                */
+       } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
+               if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+                       IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+               /*
+                * One interface hasn't rssi above threshold, bt_kill_msk must
+                * be set to default values.
+                */
+               data->reduced_tx_power = false;
+       }
+
+       /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_iterator_data data = {
+               .mvm = mvm,
+               .notif = &mvm->last_bt_notif_old,
+               .reduced_tx_power = true,
+       };
+       struct iwl_bt_coex_ci_cmd_old cmd = {};
+       u8 ci_bw_idx;
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
+       rcu_read_lock();
+       ieee80211_iterate_active_interfaces_atomic(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_bt_notif_iterator, &data);
+
+       if (data.primary) {
+               struct ieee80211_chanctx_conf *chan = data.primary;
+
+               if (WARN_ON(!chan->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_primary = 0;
+               } else {
+                       cmd.co_run_bw_primary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_primary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+       }
+
+       if (data.secondary) {
+               struct ieee80211_chanctx_conf *chan = data.secondary;
+
+               if (WARN_ON(!data.secondary->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_secondary = 0;
+               } else {
+                       cmd.co_run_bw_secondary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_secondary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+       }
+
+       rcu_read_unlock();
+
+       /* Don't spam the fw with the same command over and over */
+       if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) {
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
+                                        sizeof(cmd), &cmd))
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
+               memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
+       }
+
+       /*
+        * If there are no BSS / P2P client interfaces, reduced Tx Power is
+        * irrelevant since it is based on the RSSI coming from the beacon.
+        * Use BT_KILL_MSK_DEFAULT in that case.
+        */
+       data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+               IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+                                struct iwl_rx_cmd_buffer *rxb,
+                                struct iwl_device_cmd *dev_cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data;
+
+       IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+                      notif->bt_status ? "ON" : "OFF");
+       IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+       IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+                      le32_to_cpu(notif->primary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+                      le32_to_cpu(notif->secondary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+                      le32_to_cpu(notif->bt_activity_grading));
+       IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+                      notif->bt_agg_traffic_load);
+
+       /* remember this notification for future use: rssi fluctuations */
+       memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old));
+
+       iwl_mvm_bt_coex_notif_handle(mvm);
+
+       /*
+        * This is an async handler for a notification, returning anything other
+        * than 0 doesn't make sense even if HCMD failed.
+        */
+       return 0;
+}
+
+static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+       struct iwl_bt_iterator_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       /* If channel context is invalid or not on 2.4GHz - don't count it */
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+
+       /* This can happen if the station has been removed right now */
+       if (IS_ERR_OR_NULL(sta))
+               return;
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+       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.
+        */
+       if (!mvmsta->bt_reduced_txpower)
+               data->reduced_tx_power = false;
+       /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
+}
+
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              enum ieee80211_rssi_event rssi_event)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+       struct iwl_bt_iterator_data data = {
+               .mvm = mvm,
+               .reduced_tx_power = true,
+       };
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
+       /*
+        * Rssi update while not associated - can happen since the statistics
+        * are handled asynchronously
+        */
+       if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       /* No BT - reports should be disabled */
+       if (!mvm->last_bt_notif_old.bt_status)
+               return;
+
+       IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+                      rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+       /*
+        * Check if rssi is good enough for reduced Tx power, but not in loose
+        * scheme.
+        */
+       if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+           iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
+               ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                 false);
+       else
+               ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+       if (ret)
+               IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_bt_rssi_iterator, &data);
+
+       /*
+        * If there are no BSS / P2P client interfaces, reduced Tx Power is
+        * irrelevant since it is based on the RSSI coming from the beacon.
+        * Use BT_KILL_MSK_DEFAULT in that case.
+        */
+       data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+               IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT        (1200)
+
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       if (mvm->last_bt_notif_old.ttc_enabled)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+       if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       /* tight coex, high bt traffic, reduce AGG time limit */
+       return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+                                        struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (mvm->last_bt_notif_old.ttc_enabled)
+               return true;
+
+       if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return true;
+
+       /*
+        * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+        * since BT is already killed.
+        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+        * we Tx.
+        * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
+        */
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+       return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
+{
+       u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+       return ag == BT_OFF;
+}
+
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+                                       enum ieee80211_band band)
+{
+       u32 bt_activity =
+               le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+
+       if (band != IEEE80211_BAND_2GHZ)
+               return false;
+
+       return bt_activity >= BT_LOW_TRAFFIC;
+}
+
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm)
+{
+       iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+                                     struct iwl_rx_cmd_buffer *rxb,
+                                     struct iwl_device_cmd *dev_cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+       u8 __maybe_unused lower_bound, upper_bound;
+       int ret;
+       u8 lut;
+
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+
+       if (!IWL_MVM_BT_COEX_CORUNNING)
+               return 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return 0;
+
+       if (ant_isolation ==  mvm->last_ant_isol)
+               return 0;
+
+       for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+               if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+                       break;
+
+       lower_bound = antenna_coupling_ranges[lut].range;
+
+       if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+               upper_bound = antenna_coupling_ranges[lut + 1].range;
+       else
+               upper_bound = antenna_coupling_ranges[lut].range;
+
+       IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+                      ant_isolation, lower_bound, upper_bound, lut);
+
+       mvm->last_ant_isol = ant_isolation;
+
+       if (mvm->last_corun_lut == lut)
+               return 0;
+
+       mvm->last_corun_lut = lut;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return 0;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+                                            BT_VALID_CORUN_LUT_20 |
+                                            BT_VALID_CORUN_LUT_40);
+
+       /* For the moment, use the same LUT for 20GHz and 40GHz */
+       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
+              sizeof(bt_cmd->bt4_corun_lut20));
+
+       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
+              sizeof(bt_cmd->bt4_corun_lut40));
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
index 602bbd29ec5a70a5cb772515abdb24a032a9ea53..f131ef0ec5b30a482311a3ce9b7dec331965534b 100644 (file)
@@ -312,20 +312,69 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
                                         BT_MBOX_MSG(notif, _num, _field),  \
                                         true ? "\n" : ", ");
 
-static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
+static
+int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
+                          int pos, int bufsz)
 {
-       struct iwl_mvm *mvm = file->private_data;
-       struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
-       char *buf;
-       int ret, pos = 0, bufsz = sizeof(char) * 1024;
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
 
-       buf = kmalloc(bufsz, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+       BT_MBOX_PRINT(0, LE_PROF1, false);
+       BT_MBOX_PRINT(0, LE_PROF2, false);
+       BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+       BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+       BT_MBOX_PRINT(0, INBAND_S, false);
+       BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+       BT_MBOX_PRINT(0, LE_SCAN, false);
+       BT_MBOX_PRINT(0, LE_ADV, false);
+       BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+       BT_MBOX_PRINT(0, OPEN_CON_1, true);
 
-       mutex_lock(&mvm->mutex);
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+       BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+       BT_MBOX_PRINT(1, IP_SR, false);
+       BT_MBOX_PRINT(1, LE_MSTR, false);
+       BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+       BT_MBOX_PRINT(1, MSG_TYPE, false);
+       BT_MBOX_PRINT(1, SSN, true);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+       BT_MBOX_PRINT(2, SNIFF_ACT, false);
+       BT_MBOX_PRINT(2, PAG, false);
+       BT_MBOX_PRINT(2, INQUIRY, false);
+       BT_MBOX_PRINT(2, CONN, false);
+       BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+       BT_MBOX_PRINT(2, DISC, false);
+       BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+       BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+       BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+       BT_MBOX_PRINT(2, SCO_DURATION, true);
 
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+       BT_MBOX_PRINT(3, SCO_STATE, false);
+       BT_MBOX_PRINT(3, SNIFF_STATE, false);
+       BT_MBOX_PRINT(3, A2DP_STATE, false);
+       BT_MBOX_PRINT(3, ACL_STATE, false);
+       BT_MBOX_PRINT(3, MSTR_STATE, false);
+       BT_MBOX_PRINT(3, OBX_STATE, false);
+       BT_MBOX_PRINT(3, OPEN_CON_2, false);
+       BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+       BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+       BT_MBOX_PRINT(3, INBAND_P, false);
+       BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+       BT_MBOX_PRINT(3, SSN_2, false);
+       BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+       return pos;
+}
+
+static
+int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
+                              char *buf, int pos, int bufsz)
+{
        pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
 
        BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
@@ -378,25 +427,59 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
        BT_MBOX_PRINT(3, SSN_2, false);
        BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
-                        notif->bt_status);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
-                        notif->bt_open_conn);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
-                        notif->bt_traffic_load);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
-                        notif->bt_agg_traffic_load);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
-                        notif->bt_ci_compliance);
-       pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
-                        le32_to_cpu(notif->primary_ch_lut));
-       pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
-                        le32_to_cpu(notif->secondary_ch_lut));
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
-                        le32_to_cpu(notif->bt_activity_grading));
-       pos += scnprintf(buf+pos, bufsz-pos,
-                        "antenna isolation = %d CORUN LUT index = %d\n",
-                        mvm->last_ant_isol, mvm->last_corun_lut);
+       return pos;
+}
+
+static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char *buf;
+       int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+       buf = kmalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               struct iwl_bt_coex_profile_notif_old *notif =
+                       &mvm->last_bt_notif_old;
+
+               pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
+
+               pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+                                notif->bt_ci_compliance);
+               pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                                le32_to_cpu(notif->primary_ch_lut));
+               pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                                le32_to_cpu(notif->secondary_ch_lut));
+               pos += scnprintf(buf+pos,
+                                bufsz-pos, "bt_activity_grading = %d\n",
+                                le32_to_cpu(notif->bt_activity_grading));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "antenna isolation = %d CORUN LUT index = %d\n",
+                                mvm->last_ant_isol, mvm->last_corun_lut);
+       } else {
+               struct iwl_bt_coex_profile_notif *notif =
+                       &mvm->last_bt_notif;
+
+               pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
+
+               pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+                                notif->bt_ci_compliance);
+               pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                                le32_to_cpu(notif->primary_ch_lut));
+               pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                                le32_to_cpu(notif->secondary_ch_lut));
+               pos += scnprintf(buf+pos,
+                                bufsz-pos, "bt_activity_grading = %d\n",
+                                le32_to_cpu(notif->bt_activity_grading));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "antenna isolation = %d CORUN LUT index = %d\n",
+                                mvm->last_ant_isol, mvm->last_corun_lut);
+       }
 
        mutex_unlock(&mvm->mutex);
 
@@ -411,28 +494,48 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
                                     size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
        char buf[256];
        int bufsz = sizeof(buf);
        int pos = 0;
 
        mutex_lock(&mvm->mutex);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
-       pos += scnprintf(buf+pos, bufsz-pos,
-                      "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
-                      le64_to_cpu(cmd->bt_primary_ci),
-                      !!cmd->co_run_bw_primary);
-       pos += scnprintf(buf+pos, bufsz-pos,
-                      "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
-                      le64_to_cpu(cmd->bt_secondary_ci),
-                      !!cmd->co_run_bw_secondary);
-
-       pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
-       pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
-                        iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
-       pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
-                        iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
+
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "Channel inhibition CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tPrimary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_primary_ci));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tSecondary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_secondary_ci));
+
+               pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+                                iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+               pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+                                iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+
+       } else {
+               struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "Channel inhibition CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tPrimary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_primary_ci));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tSecondary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_secondary_ci));
+
+               pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+                                iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+               pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+                                iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+       }
 
        mutex_unlock(&mvm->mutex);
 
index 39cb33a198621f8e53926ac0ce6ac4e17996a58d..ab12aaa43034765e2c48cad43de2813b7f6ace7f 100644 (file)
  * enum iwl_bt_coex_flags - flags for BT_COEX command
  * @BT_COEX_MODE_POS:
  * @BT_COEX_MODE_MSK:
- * @BT_COEX_DISABLE:
- * @BT_COEX_2W:
- * @BT_COEX_3W:
- * @BT_COEX_NW:
- * @BT_COEX_AUTO:
- * @BT_COEX_BT: Antenna is for BT (manufacuring tests)
- * @BT_COEX_WIFI: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_DISABLE_OLD:
+ * @BT_COEX_2W_OLD:
+ * @BT_COEX_3W_OLD:
+ * @BT_COEX_NW_OLD:
+ * @BT_COEX_AUTO_OLD:
+ * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
  * @BT_COEX_SYNC2SCO:
  * @BT_COEX_CORUNNING:
  * @BT_COEX_MPLUT:
 enum iwl_bt_coex_flags {
        BT_COEX_MODE_POS                = 3,
        BT_COEX_MODE_MSK                = BITS(3) << BT_COEX_MODE_POS,
-       BT_COEX_DISABLE                 = 0x0 << BT_COEX_MODE_POS,
-       BT_COEX_2W                      = 0x1 << BT_COEX_MODE_POS,
-       BT_COEX_3W                      = 0x2 << BT_COEX_MODE_POS,
-       BT_COEX_NW                      = 0x3 << BT_COEX_MODE_POS,
-       BT_COEX_AUTO                    = 0x5 << BT_COEX_MODE_POS,
-       BT_COEX_BT                      = 0x6 << BT_COEX_MODE_POS,
-       BT_COEX_WIFI                    = 0x7 << BT_COEX_MODE_POS,
+       BT_COEX_DISABLE_OLD             = 0x0 << BT_COEX_MODE_POS,
+       BT_COEX_2W_OLD                  = 0x1 << BT_COEX_MODE_POS,
+       BT_COEX_3W_OLD                  = 0x2 << BT_COEX_MODE_POS,
+       BT_COEX_NW_OLD                  = 0x3 << BT_COEX_MODE_POS,
+       BT_COEX_AUTO_OLD                = 0x5 << BT_COEX_MODE_POS,
+       BT_COEX_BT_OLD                  = 0x6 << BT_COEX_MODE_POS,
+       BT_COEX_WIFI_OLD                = 0x7 << BT_COEX_MODE_POS,
        BT_COEX_SYNC2SCO                = BIT(7),
        BT_COEX_CORUNNING               = BIT(8),
        BT_COEX_MPLUT                   = BIT(9),
@@ -157,7 +157,7 @@ enum iwl_bt_coex_lut_type {
 #define BT_REDUCED_TX_POWER_BIT BIT(7)
 
 /**
- * struct iwl_bt_coex_cmd - bt coex configuration command
+ * struct iwl_bt_coex_cmd_old - bt coex configuration command
  * @flags:&enum iwl_bt_coex_flags
  * @max_kill:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
@@ -182,7 +182,7 @@ enum iwl_bt_coex_lut_type {
  *
  * The structure is used for the BT_COEX command.
  */
-struct iwl_bt_coex_cmd {
+struct iwl_bt_coex_cmd_old {
        __le32 flags;
        u8 max_kill;
        u8 bt_reduced_tx_power;
@@ -208,26 +208,117 @@ struct iwl_bt_coex_cmd {
        __le32 valid_bit_msk;
 } __packed; /* BT_COEX_CMD_API_S_VER_5 */
 
+enum iwl_bt_coex_mode {
+       BT_COEX_DISABLE                 = 0x0,
+       BT_COEX_NW                      = 0x1,
+       BT_COEX_BT                      = 0x2,
+       BT_COEX_WIFI                    = 0x3,
+}; /* BT_COEX_MODES_E */
+
+enum iwl_bt_coex_enabled_modules {
+       BT_COEX_MPLUT_ENABLED           = BIT(0),
+       BT_COEX_MPLUT_BOOST_ENABLED     = BIT(1),
+       BT_COEX_SYNC2SCO_ENABLED        = BIT(2),
+       BT_COEX_CORUN_ENABLED           = BIT(3),
+       BT_COEX_HIGH_BAND_RET           = BIT(4),
+}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @mode: enum %iwl_bt_coex_mode
+ * @enabled_modules: enum %iwl_bt_coex_enabled_modules
+ * @max_kill: max count of Tx retries due to kill from PTA
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @multiprio_lut: multi priority LUT configuration
+ * @mplut_prio_boost: BT priority boost registers
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+       __le32 mode;
+       __le32 enabled_modules;
+
+       __le32 max_kill;
+       __le32 override_primary_lut;
+       __le32 override_secondary_lut;
+       __le32 bt4_antenna_isolation_thr;
+
+       __le32 bt4_tx_tx_delta_freq_thr;
+       __le32 bt4_tx_rx_max_freq0;
+
+       __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+       __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
+
+       __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+} __packed; /* BT_COEX_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
+ * @corun_lut20: co-running 20 MHz LUT configuration
+ * @corun_lut40: co-running 40 MHz LUT configuration
+ *
+ * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
+ */
+struct iwl_bt_coex_corun_lut_update_cmd {
+       __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+       __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_sw_boost - SW boost values
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ */
+struct iwl_bt_coex_sw_boost {
+       __le32 wifi_tx_prio_boost;
+       __le32 wifi_rx_prio_boost;
+       __le32 kill_ack_msk;
+       __le32 kill_cts_msk;
+};
+
+/**
+ * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
+ * @boost_values: check struct  %iwl_bt_coex_sw_boost - one for each channel
+ *     primary / secondary / low priority
+ */
+struct iwl_bt_coex_sw_boost_update_cmd {
+       struct iwl_bt_coex_sw_boost boost_values[3];
+} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_reduced_txp_update_cmd
+ * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
+ *     bits are the sta_id (value)
+ */
+struct iwl_bt_coex_reduced_txp_update_cmd {
+       __le32 reduced_txp;
+} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
+
 /**
  * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
  * @bt_primary_ci:
- * @bt_secondary_ci:
- * @co_run_bw_primary:
- * @co_run_bw_secondary:
  * @primary_ch_phy_id:
+ * @bt_secondary_ci:
  * @secondary_ch_phy_id:
  *
  * Used for BT_COEX_CI command
  */
 struct iwl_bt_coex_ci_cmd {
        __le64 bt_primary_ci;
-       __le64 bt_secondary_ci;
+       __le32 primary_ch_phy_id;
 
-       u8 co_run_bw_primary;
-       u8 co_run_bw_secondary;
-       u8 primary_ch_phy_id;
-       u8 secondary_ch_phy_id;
-} __packed; /* BT_CI_MSG_API_S_VER_1 */
+       __le64 bt_secondary_ci;
+       __le32 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_2 */
 
 #define BT_MBOX(n_dw, _msg, _pos, _nbits)      \
        BT_MBOX##n_dw##_##_msg##_POS = (_pos),  \
@@ -296,35 +387,40 @@ enum iwl_bt_activity_grading {
        BT_HIGH_TRAFFIC         = 3,
 }; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
 
+enum iwl_bt_ci_compliance {
+       BT_CI_COMPLIANCE_NONE           = 0,
+       BT_CI_COMPLIANCE_PRIMARY        = 1,
+       BT_CI_COMPLIANCE_SECONDARY      = 2,
+       BT_CI_COMPLIANCE_BOTH           = 3,
+}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
+
+#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id)   \
+               (_ttc_rrc_status & BIT(_phy_id))
+
+#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id)   \
+               ((_ttc_rrc_status >> 4) & BIT(_phy_id))
+
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
  * @mbox_msg: message from BT to WiFi
  * @msg_idx: the index of the message
- * @bt_status: 0 - off, 1 - on
- * @bt_open_conn: number of BT connections open
- * @bt_traffic_load: load of BT traffic
- * @bt_agg_traffic_load: aggregated load of BT traffic
- * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
- * @ttc_enabled: true if ttc has been enabled by the firmware
- * @primary_ch_lut: LUT used for primary channel
- * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_ci_compliance: enum %iwl_bt_ci_compliance
+ * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
+ * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
  * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
  */
 struct iwl_bt_coex_profile_notif {
        __le32 mbox_msg[4];
        __le32 msg_idx;
-       u8 bt_status;
-       u8 bt_open_conn;
-       u8 bt_traffic_load;
-       u8 bt_agg_traffic_load;
-       u8 bt_ci_compliance;
-       u8 ttc_enabled;
-       __le16 reserved;
+       __le32 bt_ci_compliance;
 
        __le32 primary_ch_lut;
        __le32 secondary_ch_lut;
        __le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+       u8 ttc_rrc_status;
+       u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
 
 enum iwl_bt_coex_prio_table_event {
        BT_COEX_PRIO_TBL_EVT_INIT_CALIB1                = 0,
@@ -363,4 +459,54 @@ struct iwl_bt_coex_prio_tbl_cmd {
        u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
 } __packed;
 
+/**
+ * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd_old {
+       __le64 bt_primary_ci;
+       __le64 bt_secondary_ci;
+
+       u8 co_run_bw_primary;
+       u8 co_run_bw_secondary;
+       u8 primary_ch_phy_id;
+       u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_profile_notif_old - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ */
+struct iwl_bt_coex_profile_notif_old {
+       __le32 mbox_msg[4];
+       __le32 msg_idx;
+       u8 bt_status;
+       u8 bt_open_conn;
+       u8 bt_traffic_load;
+       u8 bt_agg_traffic_load;
+       u8 bt_ci_compliance;
+       u8 ttc_enabled;
+       __le16 reserved;
+
+       __le32 primary_ch_lut;
+       __le32 secondary_ch_lut;
+       __le32 bt_activity_grading;
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+
 #endif /* __fw_api_bt_coex_h__ */
index 1d586923d5b767c0aaaf34b09705c835829d4082..c02a9e45ec5eaec78ce9ef49c47957c6fcd2c7da 100644 (file)
@@ -509,7 +509,7 @@ struct iwl_scan_offload_profile_cfg {
  * @full_scan_mul:     number of partial scans before each full scan
  */
 struct iwl_scan_offload_schedule {
-       u16 delay;
+       __le16 delay;
        u8 iterations;
        u8 full_scan_mul;
 } __packed;
@@ -582,4 +582,211 @@ struct iwl_sched_scan_results {
        u8 reserved;
 };
 
+/* Unified LMAC scan API */
+
+#define IWL_MVM_BASIC_PASSIVE_DWELL 110
+
+/**
+ * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ *     cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_req_tx_cmd {
+       __le32 tx_flags;
+       __le32 rate_n_flags;
+       u8 sta_id;
+       u8 reserved[3];
+} __packed;
+
+enum iwl_scan_channel_flags_lmac {
+       IWL_UNIFIED_SCAN_CHANNEL_FULL           = BIT(27),
+       IWL_UNIFIED_SCAN_CHANNEL_PARTIAL        = BIT(28),
+};
+
+/**
+ * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
+ * @flags:             bits 1-20: directed scan to i'th ssid
+ *                     other bits &enum iwl_scan_channel_flags_lmac
+ * @channel_number:    channel number 1-13 etc
+ * @iter_count:                scan iteration on this channel
+ * @iter_interval:     interval in seconds between iterations on one channel
+ */
+struct iwl_scan_channel_cfg_lmac {
+       __le32 flags;
+       __le16 channel_num;
+       __le16 iter_count;
+       __le32 iter_interval;
+} __packed;
+
+/*
+ * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
+ * @offset: offset in the data block
+ * @len: length of the segment
+ */
+struct iwl_scan_probe_segment {
+       __le16 offset;
+       __le16 len;
+} __packed;
+
+/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
+ * @mac_header: first (and common) part of the probe
+ * @band_data: band specific data
+ * @common_data: last (and common) part of the probe
+ * @buf: raw data block
+ */
+struct iwl_scan_probe_req {
+       struct iwl_scan_probe_segment mac_header;
+       struct iwl_scan_probe_segment band_data[2];
+       struct iwl_scan_probe_segment common_data;
+       u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
+} __packed;
+
+enum iwl_scan_channel_flags {
+       IWL_SCAN_CHANNEL_FLAG_EBS               = BIT(0),
+       IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE      = BIT(1),
+       IWL_SCAN_CHANNEL_FLAG_CACHE_ADD         = BIT(2),
+};
+
+/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
+ * @flags: enum iwl_scan_channel_flgs
+ * @non_ebs_ratio: how many regular scan iteration before EBS
+ */
+struct iwl_scan_channel_opt {
+       __le16 flags;
+       __le16 non_ebs_ratio;
+} __packed;
+
+/**
+ * iwl_mvm_lmac_scan_flags
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
+ *     without filtering.
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
+ * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
+ * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
+ * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
+ * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
+ */
+enum iwl_mvm_lmac_scan_flags {
+       IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL         = BIT(0),
+       IWL_MVM_LMAC_SCAN_FLAG_PASSIVE          = BIT(1),
+       IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION   = BIT(2),
+       IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE    = BIT(3),
+       IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS   = BIT(4),
+       IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED       = BIT(5),
+};
+
+enum iwl_scan_priority {
+       IWL_SCAN_PRIORITY_LOW,
+       IWL_SCAN_PRIORITY_MEDIUM,
+       IWL_SCAN_PRIORITY_HIGH,
+};
+
+/**
+ * iwl_scan_req_unified_lmac - SCAN_REQUEST_CMD_API_S_VER_1
+ * @reserved1: for alignment and future use
+ * @channel_num: num of channels to scan
+ * @active-dwell: dwell time for active channels
+ * @passive-dwell: dwell time for passive channels
+ * @fragmented-dwell: dwell time for fragmented passive scan
+ * @reserved2: for alignment and future use
+ * @rx_chain_selct: PHY_RX_CHAIN_* flags
+ * @scan_flags: &enum iwl_mvm_lmac_scan_flags
+ * @max_out_time: max time (in TU) to be out of associated channel
+ * @suspend_time: pause scan this long (TUs) when returning to service channel
+ * @flags: RXON flags
+ * @filter_flags: RXON filter
+ * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
+ * @direct_scan: list of SSIDs for directed active scan
+ * @scan_prio: enum iwl_scan_priority
+ * @iter_num: number of scan iterations
+ * @delay: delay in seconds before first iteration
+ * @schedule: two scheduling plans. The first one is finite, the second one can
+ *     be infinite.
+ * @channel_opt: channel optimization options, for full and partial scan
+ * @data: channel configuration and probe request packet.
+ */
+struct iwl_scan_req_unified_lmac {
+       /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
+       __le32 reserved1;
+       u8 n_channels;
+       u8 active_dwell;
+       u8 passive_dwell;
+       u8 fragmented_dwell;
+       __le16 reserved2;
+       __le16 rx_chain_select;
+       __le32 scan_flags;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 flags;
+       __le32 filter_flags;
+       struct iwl_scan_req_tx_cmd tx_cmd[2];
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 scan_prio;
+       /* SCAN_REQ_PERIODIC_PARAMS_API_S */
+       __le32 iter_num;
+       __le32 delay;
+       struct iwl_scan_offload_schedule schedule[2];
+       struct iwl_scan_channel_opt channel_opt[2];
+       u8 data[];
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_results_notif - scan results for one channel -
+ *     SCAN_RESULT_NTF_API_S_VER_3
+ * @channel: which channel the results are from
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
+ * @num_probe_not_sent: # of request that weren't sent due to not enough time
+ * @duration: duration spent in channel, in usecs
+ */
+struct iwl_lmac_scan_results_notif {
+       u8 channel;
+       u8 band;
+       u8 probe_status;
+       u8 num_probe_not_sent;
+       __le32 duration;
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
+ *     SCAN_COMPLETE_NTF_API_S_VER_3
+ * @scanned_channels: number of channels scanned (and number of valid results)
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: an array of scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_lmac_scan_complete_notif {
+       u8 scanned_channels;
+       u8 status;
+       u8 bt_status;
+       u8 last_channel;
+       __le32 tsf_low;
+       __le32 tsf_high;
+       struct iwl_scan_results_notif results[];
+} __packed;
+
+/**
+ * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
+ * @last_schedule_line: last schedule line executed (fast or regular)
+ * @last_schedule_iteration: last scan iteration executed before scan abort
+ * @status: enum iwl_scan_offload_complete_status
+ * @ebs_status: EBS success status &enum iwl_scan_ebs_status
+ * @time_after_last_iter; time in seconds elapsed after last iteration
+ */
+struct iwl_periodic_scan_complete {
+       u8 last_schedule_line;
+       u8 last_schedule_iteration;
+       u8 status;
+       u8 ebs_status;
+       __le32 time_after_last_iter;
+       __le32 reserved;
+} __packed;
+
 #endif
index bdd6ff648626a0e8435c60b8287e274098f8fc69..d6073f67b212e12c2c7565cadf44c50268763cd8 100644 (file)
  * @TX_CMD_FLG_ACK: expect ACK from receiving station
  * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
  *     Otherwise, use rate_n_flags from the TX command
- * @TX_CMD_FLG_BA: this frame is a block ack
  * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
  *     Must set TX_CMD_FLG_ACK with this flag.
- * @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
  * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
  * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
  * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
  * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
  *     Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
  * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
- * @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
  * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
  *     Should be set for beacons and probe responses
  * @TX_CMD_FLG_CALIB: activate PA TX power calibrations
  * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
- * @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
  * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
  *     Should be set for 26/30 length MAC headers
  * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
@@ -103,7 +99,6 @@ enum iwl_tx_flags {
        TX_CMD_FLG_PROT_REQUIRE         = BIT(0),
        TX_CMD_FLG_ACK                  = BIT(3),
        TX_CMD_FLG_STA_RATE             = BIT(4),
-       TX_CMD_FLG_BA                   = BIT(5),
        TX_CMD_FLG_BAR                  = BIT(6),
        TX_CMD_FLG_TXOP_PROT            = BIT(7),
        TX_CMD_FLG_VHT_NDPA             = BIT(8),
@@ -113,11 +108,9 @@ enum iwl_tx_flags {
        TX_CMD_FLG_BT_DIS               = BIT(12),
        TX_CMD_FLG_SEQ_CTL              = BIT(13),
        TX_CMD_FLG_MORE_FRAG            = BIT(14),
-       TX_CMD_FLG_NEXT_FRAME           = BIT(15),
        TX_CMD_FLG_TSF                  = BIT(16),
        TX_CMD_FLG_CALIB                = BIT(17),
        TX_CMD_FLG_KEEP_SEQ_CTL         = BIT(18),
-       TX_CMD_FLG_AGG_START            = BIT(19),
        TX_CMD_FLG_MH_PAD               = BIT(20),
        TX_CMD_FLG_RESP_TO_DRV          = BIT(21),
        TX_CMD_FLG_CCMP_AGG             = BIT(22),
@@ -191,8 +184,6 @@ enum iwl_tx_flags {
  * struct iwl_tx_cmd - TX command struct to FW
  * ( TX_CMD = 0x1c )
  * @len: in bytes of the payload, see below for details
- * @next_frame_len: same as len, but for next frame (0 if not applicable)
- *     Used for fragmentation and bursting, but not in 11n aggregation.
  * @tx_flags: combination of TX_CMD_FLG_*
  * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
  *     cleared. Combination of RATE_MCS_*
@@ -210,8 +201,6 @@ enum iwl_tx_flags {
  * @data_retry_limit: max attempts to send the data packet
  * @tid_spec: TID/tspec
  * @pm_frame_timeout: PM TX frame timeout
- * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
- *     specified by HCCA protocol
  *
  * The byte count (both len and next_frame_len) includes MAC header
  * (24/26/30/32 bytes)
@@ -241,8 +230,7 @@ struct iwl_tx_cmd {
        u8 initial_rate_index;
        u8 reserved2;
        u8 key[16];
-       __le16 next_frame_flags;
-       __le16 reserved3;
+       __le32 reserved3;
        __le32 life_time;
        __le32 dram_lsb_ptr;
        u8 dram_msb_ptr;
@@ -250,7 +238,7 @@ struct iwl_tx_cmd {
        u8 data_retry_limit;
        u8 tid_tspec;
        __le16 pm_frame_timeout;
-       __le16 driver_txop;
+       __le16 reserved4;
        u8 payload[0];
        struct ieee80211_hdr hdr[0];
 } __packed; /* TX_CMD_API_S_VER_3 */
index 309a9b9a94fecc26918f967e7b9e7a01374d43b3..b8e4e78d601b98b6e67a444f5f7cb09985a7bef1 100644 (file)
@@ -86,6 +86,8 @@ enum {
 
 #define IWL_MVM_STATION_COUNT  16
 
+#define IWL_MVM_TDLS_STA_COUNT 4
+
 /* commands */
 enum {
        MVM_ALIVE = 0x1,
@@ -135,6 +137,7 @@ enum {
        SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
        SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
        MATCH_FOUND_NOTIFICATION = 0xd9,
+       SCAN_ITERATION_COMPLETE = 0xe7,
 
        /* Phy */
        PHY_CONFIGURATION_CMD = 0x6a,
@@ -163,7 +166,6 @@ enum {
        BEACON_NOTIFICATION = 0x90,
        BEACON_TEMPLATE_CMD = 0x91,
        TX_ANT_CONFIGURATION_CMD = 0x98,
-       BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
        EOSP_NOTIFICATION = 0x9e,
        REDUCE_TX_POWER_CMD = 0x9f,
@@ -185,6 +187,10 @@ enum {
        BT_COEX_PRIO_TABLE = 0xcc,
        BT_COEX_PROT_ENV = 0xcd,
        BT_PROFILE_NOTIFICATION = 0xce,
+       BT_CONFIG = 0x9b,
+       BT_COEX_UPDATE_SW_BOOST = 0x5a,
+       BT_COEX_UPDATE_CORUN_LUT = 0x5b,
+       BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
        BT_COEX_CI = 0x5d,
 
        REPLY_SF_CFG_CMD = 0xd1,
@@ -534,6 +540,9 @@ enum iwl_time_event_type {
        /* WiDi Sync Events */
        TE_WIDI_TX_SYNC,
 
+       /* Channel Switch NoA */
+       TE_P2P_GO_CSA_NOA,
+
        TE_MAX
 }; /* MAC_EVENT_TYPE_API_E_VER_1 */
 
index aad36212c4d7dca6942ef7d31e4d9f1f8d572c5a..96b9cf8137e7f158df7e7db16cc32a779ca1f2be 100644 (file)
@@ -67,6 +67,7 @@
 #include "iwl-prph.h"
 #include "fw-api.h"
 #include "mvm.h"
+#include "time-event.h"
 
 const u8 iwl_mvm_ac_to_tx_fifo[] = {
        IWL_MVM_TX_FIFO_VO,
@@ -667,10 +668,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
        if (vif->bss_conf.qos)
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
 
-       if (vif->bss_conf.use_cts_prot) {
+       if (vif->bss_conf.use_cts_prot)
                cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
-               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
-       }
+
        IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
                       vif->bss_conf.use_cts_prot,
                       vif->bss_conf.ht_operation_mode);
@@ -970,7 +970,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
        WARN_ON(vif->type != NL80211_IFTYPE_AP &&
                vif->type != NL80211_IFTYPE_ADHOC);
 
-       beacon = ieee80211_beacon_get(mvm->hw, vif);
+       beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
        if (!beacon)
                return -ENOMEM;
 
@@ -1201,12 +1201,43 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return 0;
 }
 
+static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *csa_vif, u32 gp2)
+{
+       struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(csa_vif);
+
+       if (!ieee80211_csa_is_complete(csa_vif)) {
+               int c = ieee80211_csa_update_counter(csa_vif);
+
+               iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
+               if (csa_vif->p2p &&
+                   !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+                       u32 rel_time = (c + 1) *
+                                      csa_vif->bss_conf.beacon_int -
+                                      IWL_MVM_CHANNEL_SWITCH_TIME;
+                       u32 apply_time = gp2 + rel_time * 1024;
+
+                       iwl_mvm_schedule_csa_noa(mvm, csa_vif,
+                                                IWL_MVM_CHANNEL_SWITCH_TIME -
+                                                IWL_MVM_CHANNEL_SWITCH_MARGIN,
+                                                apply_time);
+               }
+       } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
+               /* we don't have CSA NoA scheduled yet, switch now */
+               ieee80211_csa_finish(csa_vif);
+               RCU_INIT_POINTER(mvm->csa_vif, NULL);
+       }
+}
+
 int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                            struct iwl_rx_cmd_buffer *rxb,
                            struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_mvm_tx_resp *beacon_notify_hdr;
+       struct ieee80211_vif *csa_vif;
+       struct ieee80211_vif *tx_blocked_vif;
        u64 tsf;
 
        lockdep_assert_held(&mvm->mutex);
@@ -1232,12 +1263,32 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                     mvm->ap_last_beacon_gp2,
                     le32_to_cpu(beacon_notify_hdr->initial_rate));
 
-       if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
-               if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
-                       iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
-               } else {
-                       ieee80211_csa_finish(mvm->csa_vif);
-                       mvm->csa_vif = NULL;
+       csa_vif = rcu_dereference_protected(mvm->csa_vif,
+                                           lockdep_is_held(&mvm->mutex));
+       if (unlikely(csa_vif && csa_vif->csa_active))
+               iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+
+       tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
+                                               lockdep_is_held(&mvm->mutex));
+       if (unlikely(tx_blocked_vif)) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(tx_blocked_vif);
+
+               /*
+                * The channel switch is started and we have blocked the
+                * stations. If this is the first beacon (the timeout wasn't
+                * set), set the unblock timeout, otherwise countdown
+                */
+               if (!mvm->csa_tx_block_bcn_timeout)
+                       mvm->csa_tx_block_bcn_timeout =
+                               IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
+               else
+                       mvm->csa_tx_block_bcn_timeout--;
+
+               /* Check if the timeout is expired, and unblock tx */
+               if (mvm->csa_tx_block_bcn_timeout == 0) {
+                       iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
+                       RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
                }
        }
 
index 72e3146625f8f61f7c4c8f98111a7b980546eab4..2eb6ebee446708308168da65d926f394e743597d 100644 (file)
@@ -80,6 +80,8 @@
 #include "fw-api-scan.h"
 #include "iwl-phy-db.h"
 #include "testmode.h"
+#include "iwl-fw-error-dump.h"
+#include "iwl-prph.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -241,6 +243,21 @@ iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
        }
 }
 
+static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       iwl_mvm_ref(mvm, ref_type);
+
+       if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+                               !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
+                               HZ)) {
+               WARN_ON_ONCE(1);
+               iwl_mvm_unref(mvm, ref_type);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
 {
        int i;
@@ -276,6 +293,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
+                   IEEE80211_HW_CHANCTX_STA_CSA |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
                    IEEE80211_HW_SUPPORTS_STATIC_SMPS;
 
@@ -303,6 +321,16 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
        }
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+           !iwlwifi_mod_params.uapsd_disable) {
+               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
+               hw->uapsd_queues = IWL_UAPSD_AC_INFO;
+               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+       }
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
        hw->chanctx_data_size = sizeof(u16);
@@ -550,9 +578,6 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
        case IEEE80211_AMPDU_TX_OPERATIONAL:
-               iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
-               tx_agg_ref = true;
-
                /*
                 * for tx start, wait synchronously until D0i3 exit to
                 * get the correct sequence number for the tid.
@@ -561,12 +586,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                 * by the trans layer (unlike commands), so wait for
                 * d0i3 exit in these cases as well.
                 */
-               if (!wait_event_timeout(mvm->d0i3_exit_waitq,
-                         !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
-                       WARN_ON_ONCE(1);
-                       iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
-                       return -EIO;
-               }
+               ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
+               if (ret)
+                       return ret;
+
+               tx_agg_ref = true;
                break;
        default:
                break;
@@ -638,6 +662,104 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        mvmvif->phy_ctxt = NULL;
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
+{
+       struct iwl_fw_error_dump_file *dump_file;
+       struct iwl_fw_error_dump_data *dump_data;
+       struct iwl_fw_error_dump_info *dump_info;
+       const struct fw_img *img;
+       u32 sram_len, sram_ofs;
+       u32 file_len, rxf_len;
+       unsigned long flags;
+       u32 trans_len;
+       int reg_val;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->fw_error_dump)
+               return;
+
+       img = &mvm->fw->img[mvm->cur_ucode];
+       sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       /* reading buffer size */
+       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
+       rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+
+       /* the register holds the value divided by 128 */
+       rxf_len = rxf_len << 7;
+
+       file_len = sizeof(*dump_file) +
+                  sizeof(*dump_data) * 3 +
+                  sram_len +
+                  rxf_len +
+                  sizeof(*dump_info);
+
+       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
+       if (trans_len)
+               file_len += trans_len;
+
+       dump_file = vzalloc(file_len);
+       if (!dump_file)
+               return;
+
+       mvm->fw_error_dump = dump_file;
+
+       dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+       dump_file->file_len = cpu_to_le32(file_len);
+       dump_data = (void *)dump_file->data;
+
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
+       dump_data->len = cpu_to_le32(sizeof(*dump_info));
+       dump_info = (void *) dump_data->data;
+       dump_info->device_family =
+               mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
+                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
+                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+       memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
+              sizeof(dump_info->fw_human_readable));
+       strncpy(dump_info->dev_human_readable, mvm->cfg->name,
+               sizeof(dump_info->dev_human_readable));
+       strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
+               sizeof(dump_info->bus_human_readable));
+
+       dump_data = iwl_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+       dump_data->len = cpu_to_le32(rxf_len);
+
+       if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
+               u32 *rxf = (void *)dump_data->data;
+               int i;
+
+               for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
+                       iwl_trans_write_prph(mvm->trans,
+                                            RXF_LD_FENCE_OFFSET_ADDR,
+                                            i * sizeof(u32));
+                       rxf[i] = iwl_trans_read_prph(mvm->trans,
+                                                    RXF_FIFO_RD_FENCE_ADDR);
+               }
+               iwl_trans_release_nic_access(mvm->trans, &flags);
+       }
+
+       dump_data = iwl_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
+       dump_data->len = cpu_to_le32(sram_len);
+       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
+                                sram_len);
+
+       if (trans_len) {
+               void *buf = iwl_fw_error_next_data(dump_data);
+               u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
+                                                        trans_len);
+               dump_data = (void *)((u8 *)buf + real_trans_len);
+               dump_file->file_len =
+                       cpu_to_le32(file_len - trans_len + real_trans_len);
+       }
+}
+#endif
+
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -796,6 +918,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
+       /*
+        * make sure D0i3 exit is completed, otherwise a target access
+        * during tx queue configuration could be done when still in
+        * D0i3 state.
+        */
+       ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
+       if (ret)
+               return ret;
+
        /*
         * Not much to do here. The stack will not allow interface
         * types or combinations that we didn't advertise, so we
@@ -910,6 +1041,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
  out_unlock:
        mutex_unlock(&mvm->mutex);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
+
        return ret;
 }
 
@@ -1170,8 +1303,12 @@ static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
 
        bcast_mac = &cmd->macs[mvmvif->id];
 
-       /* enable filtering only for associated stations */
-       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+       /*
+        * enable filtering only for associated stations, but not for P2P
+        * Clients
+        */
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
+           !vif->bss_conf.assoc)
                return;
 
        bcast_mac->default_discard = 1;
@@ -1248,10 +1385,6 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
                return 0;
 
-       /* bcast filtering isn't supported for P2P client */
-       if (vif->p2p)
-               return 0;
-
        if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
                return 0;
 
@@ -1289,7 +1422,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        /* add quota for this interface */
-                       ret = iwl_mvm_update_quotas(mvm, vif);
+                       ret = iwl_mvm_update_quotas(mvm, NULL);
                        if (ret) {
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
@@ -1436,7 +1569,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        /* power updated needs to be done before quotas */
        iwl_mvm_power_update_mac(mvm);
 
-       ret = iwl_mvm_update_quotas(mvm, vif);
+       ret = iwl_mvm_update_quotas(mvm, NULL);
        if (ret)
                goto out_quota_failed;
 
@@ -1474,6 +1607,18 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       /* Handle AP stop while in CSA */
+       if (rcu_access_pointer(mvm->csa_vif) == vif) {
+               iwl_mvm_remove_time_event(mvm, mvmvif,
+                                         &mvmvif->time_event_data);
+               RCU_INIT_POINTER(mvm->csa_vif, NULL);
+       }
+
+       if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
+               RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
+               mvm->csa_tx_block_bcn_timeout = 0;
+       }
+
        mvmvif->ap_ibss_active = false;
        mvm->ap_last_beacon_gp2 = 0;
 
@@ -1529,7 +1674,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
        mutex_lock(&mvm->mutex);
 
        if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
-               iwl_mvm_sched_scan_stop(mvm, true);
+               iwl_mvm_scan_offload_stop(mvm, true);
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
@@ -1563,7 +1708,7 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        switch (mvm->scan_status) {
        case IWL_MVM_SCAN_SCHED:
-               ret = iwl_mvm_sched_scan_stop(mvm, true);
+               ret = iwl_mvm_scan_offload_stop(mvm, true);
                if (ret) {
                        ret = -EBUSY;
                        goto out;
@@ -1578,7 +1723,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-       ret = iwl_mvm_scan_request(mvm, vif, req);
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
+       else
+               ret = iwl_mvm_scan_request(mvm, vif, req);
+
        if (ret)
                iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 out:
@@ -1694,6 +1843,70 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       int count = 0;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta || IS_ERR(sta) || !sta->tdls)
+                       continue;
+
+               if (vif) {
+                       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+                       if (mvmsta->vif != vif)
+                               continue;
+               }
+
+               count++;
+       }
+
+       return count;
+}
+
+static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif,
+                                     bool sta_added)
+{
+       int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
+
+       /*
+        * Disable ps when the first TDLS sta is added and re-enable it
+        * when the last TDLS sta is removed
+        */
+       if ((tdls_sta_cnt == 1 && sta_added) ||
+           (tdls_sta_cnt == 0 && !sta_added))
+               iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta || IS_ERR(sta) || !sta->tdls)
+                       continue;
+
+               mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
+                               NL80211_TDLS_TEARDOWN,
+                               WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
+                               GFP_KERNEL);
+       }
+}
+
 static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -1732,7 +1945,20 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                        ret = -EINVAL;
                        goto out_unlock;
                }
+
+               if (sta->tdls &&
+                   (vif->p2p ||
+                    iwl_mvm_tdls_sta_count(mvm, NULL) ==
+                                               IWL_MVM_TDLS_STA_COUNT ||
+                    iwl_mvm_phy_ctx_count(mvm) > 1)) {
+                       IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+
                ret = iwl_mvm_add_sta(mvm, vif, sta);
+               if (sta->tdls && ret == 0)
+                       iwl_mvm_recalc_tdls_state(mvm, vif, true);
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
                /*
@@ -1750,6 +1976,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                             true);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
+
+               /* we don't support TDLS during DCM */
+               if (iwl_mvm_phy_ctx_count(mvm) > 1)
+                       iwl_mvm_teardown_tdls_peers(mvm);
+
                /* enable beacon filtering */
                WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
                ret = 0;
@@ -1767,6 +1998,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_NOTEXIST) {
                ret = iwl_mvm_rm_sta(mvm, vif, sta);
+               if (sta->tdls)
+                       iwl_mvm_recalc_tdls_state(mvm, vif, false);
        } else {
                ret = -EIO;
        }
@@ -1838,6 +2071,18 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+                                                 struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
+
+       mutex_lock(&mvm->mutex);
+       /* Protect the session to hear the TDLS setup response on the channel */
+       iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
+       mutex_unlock(&mvm->mutex);
+}
+
 static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
                                        struct cfg80211_sched_scan_request *req,
@@ -1879,15 +2124,21 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
 
        mvm->scan_status = IWL_MVM_SCAN_SCHED;
 
-       ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
-       if (ret)
-               goto err;
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+               ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+               if (ret)
+                       goto err;
+       }
 
        ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
        if (ret)
                goto err;
 
-       ret = iwl_mvm_sched_scan_start(mvm, req);
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
+       else
+               ret = iwl_mvm_sched_scan_start(mvm, req);
+
        if (!ret)
                goto out;
 err:
@@ -1906,7 +2157,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        int ret;
 
        mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_sched_scan_stop(mvm, false);
+       ret = iwl_mvm_scan_offload_stop(mvm, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
 
@@ -2140,17 +2391,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
        return 0;
 }
 
-static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
-                              struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
+                                struct ieee80211_chanctx_conf *ctx)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
        struct iwl_mvm_phy_ctxt *phy_ctxt;
        int ret;
 
+       lockdep_assert_held(&mvm->mutex);
+
        IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
 
-       mutex_lock(&mvm->mutex);
        phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
        if (!phy_ctxt) {
                ret = -ENOSPC;
@@ -2168,19 +2419,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
        *phy_ctxt_id = phy_ctxt->id;
 out:
+       return ret;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+       ret = __iwl_mvm_add_chanctx(mvm, ctx);
        mutex_unlock(&mvm->mutex);
+
        return ret;
 }
 
+static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
+                                    struct ieee80211_chanctx_conf *ctx)
+{
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+}
+
 static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
                                   struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
-       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+       __iwl_mvm_remove_chanctx(mvm, ctx);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2209,17 +2481,17 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif *vif,
-                                     struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx,
+                                       bool switching_chanctx)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
        struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       mutex_lock(&mvm->mutex);
+       lockdep_assert_held(&mvm->mutex);
 
        mvmvif->phy_ctxt = phy_ctxt;
 
@@ -2236,18 +2508,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                 * (in bss_info_changed), similarly for IBSS.
                 */
                ret = 0;
-               goto out_unlock;
+               goto out;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_MONITOR:
                break;
        default:
                ret = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        ret = iwl_mvm_binding_add_vif(mvm, vif);
        if (ret)
-               goto out_unlock;
+               goto out;
 
        /*
         * Power state must be updated before quotas,
@@ -2261,67 +2533,164 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
                mvmvif->monitor_active = true;
-               ret = iwl_mvm_update_quotas(mvm, vif);
+               ret = iwl_mvm_update_quotas(mvm, NULL);
                if (ret)
                        goto out_remove_binding;
        }
 
        /* Handle binding during CSA */
-       if (vif->type == NL80211_IFTYPE_AP) {
-               iwl_mvm_update_quotas(mvm, vif);
+       if ((vif->type == NL80211_IFTYPE_AP) ||
+           (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+               iwl_mvm_update_quotas(mvm, NULL);
                iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        }
 
-       goto out_unlock;
+       goto out;
 
- out_remove_binding:
+out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
        iwl_mvm_power_update_mac(mvm);
- out_unlock:
-       mutex_unlock(&mvm->mutex);
+out:
        if (ret)
                mvmvif->phy_ctxt = NULL;
        return ret;
 }
-
-static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        struct ieee80211_chanctx_conf *ctx)
+static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
 
        mutex_lock(&mvm->mutex);
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_chanctx_conf *ctx,
+                                          bool switching_chanctx)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_vif *disabled_vif = NULL;
+
+       lockdep_assert_held(&mvm->mutex);
 
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
        switch (vif->type) {
        case NL80211_IFTYPE_ADHOC:
-               goto out_unlock;
+               goto out;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
-               iwl_mvm_update_quotas(mvm, NULL);
                break;
        case NL80211_IFTYPE_AP:
                /* This part is triggered only during CSA */
                if (!vif->csa_active || !mvmvif->ap_ibss_active)
-                       goto out_unlock;
+                       goto out;
+
+               /* Set CS bit on all the stations */
+               iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
+
+               /* Save blocked iface, the timeout is set on the next beacon */
+               rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
 
                mvmvif->ap_ibss_active = false;
-               iwl_mvm_update_quotas(mvm, NULL);
-               /*TODO: bt_coex notification here? */
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!switching_chanctx)
+                       break;
+
+               disabled_vif = vif;
+
+               iwl_mvm_mac_ctxt_changed(mvm, vif, true);
+               break;
        default:
                break;
        }
 
+       iwl_mvm_update_quotas(mvm, disabled_vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
-out_unlock:
+out:
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
        mutex_unlock(&mvm->mutex);
 }
 
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif_chanctx_switch *vifs,
+                                     int n_vifs,
+                                     enum ieee80211_chanctx_switch_mode mode)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       /* we only support SWAP_CONTEXTS and with a single-vif right now */
+       if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&mvm->mutex);
+       __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+       __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
+
+       ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
+       if (ret) {
+               IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
+               goto out_reassign;
+       }
+
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+                                          true);
+       if (ret) {
+               IWL_ERR(mvm,
+                       "failed to assign new_ctx during channel switch\n");
+               goto out_remove;
+       }
+
+       goto out;
+
+out_remove:
+       __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
+
+out_reassign:
+       ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
+       if (ret) {
+               IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
+               goto out_restart;
+       }
+
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+                                          true);
+       if (ret) {
+               IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+               goto out_restart;
+       }
+
+       goto out;
+
+out_restart:
+       /* things keep failing, better restart the hw */
+       iwl_mvm_nic_restart(mvm, false);
+
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
 static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
                           struct ieee80211_sta *sta,
                           bool set)
@@ -2409,15 +2778,19 @@ static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
                                          struct cfg80211_chan_def *chandef)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct ieee80211_vif *csa_vif;
 
        mutex_lock(&mvm->mutex);
-       if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
+
+       csa_vif = rcu_dereference_protected(mvm->csa_vif,
+                                           lockdep_is_held(&mvm->mutex));
+       if (WARN(csa_vif && csa_vif->csa_active,
                 "Another CSA is already in progress"))
                goto out_unlock;
 
        IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
                           chandef->center_freq1);
-       mvm->csa_vif = vif;
+       rcu_assign_pointer(mvm->csa_vif, vif);
 
 out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -2474,6 +2847,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
        .flush = iwl_mvm_mac_flush,
        .sched_scan_start = iwl_mvm_mac_sched_scan_start,
        .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
@@ -2486,6 +2860,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .change_chanctx = iwl_mvm_change_chanctx,
        .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
        .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+       .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
 
        .start_ap = iwl_mvm_start_ap_ibss,
        .stop_ap = iwl_mvm_stop_ap_ibss,
index f7e54a57f46d1dae792661fe836510639f01ca74..785e5232c757f4e4647f723fab5729bd141368eb 100644 (file)
 #define IWL_RSSI_OFFSET 50
 #define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
 
+/*
+ * The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
+ * TBTT. This value should be big enough to ensure that we switch in time.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME 40
+
+/*
+ * This value (in TUs) is used to fine tune the CSA NoA end time which should
+ * be just before "beacon 0" TBTT.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
+
+/*
+ * Number of beacons to transmit on a new channel until we unblock tx to
+ * the stations, even if we didn't identify them on a new channel
+ */
+#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
+
 enum iwl_mvm_tx_fifo {
        IWL_MVM_TX_FIFO_BK = 0,
        IWL_MVM_TX_FIFO_BE,
@@ -230,6 +248,7 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_USER,
        IWL_MVM_REF_TX,
        IWL_MVM_REF_TX_AGG,
+       IWL_MVM_REF_ADD_IF,
        IWL_MVM_REF_EXIT_WORK,
 
        IWL_MVM_REF_COUNT,
@@ -532,7 +551,7 @@ struct iwl_mvm {
 
        /* Scan status, cmd (pre-allocated) and auxiliary station */
        enum iwl_scan_status scan_status;
-       struct iwl_scan_cmd *scan_cmd;
+       void *scan_cmd;
        struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
        /* rx chain antennas set through debugfs for the scan command */
@@ -595,10 +614,6 @@ struct iwl_mvm {
        /* -1 for always, 0 for never, >0 for that many times */
        s8 restart_fw;
        void *fw_error_dump;
-       void *fw_error_sram;
-       u32 fw_error_sram_len;
-       u32 *fw_error_rxf;
-       u32 fw_error_rxf_len;
 
 #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
@@ -633,8 +648,12 @@ struct iwl_mvm {
 
        /* BT-Coex */
        u8 bt_kill_msk;
+
+       struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
+       struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
        struct iwl_bt_coex_profile_notif last_bt_notif;
        struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
+
        u32 last_ant_isol;
        u8 last_corun_lut;
        u8 bt_tx_prio;
@@ -657,7 +676,9 @@ struct iwl_mvm {
        /* Indicate if device power save is allowed */
        bool ps_disabled;
 
-       struct ieee80211_vif *csa_vif;
+       struct ieee80211_vif __rcu *csa_vif;
+       struct ieee80211_vif __rcu *csa_tx_blocked_vif;
+       u8 csa_tx_block_bcn_timeout;
 
        /* system time of last beacon (for AP/GO interface) */
        u32 ap_last_beacon_gp2;
@@ -732,11 +753,6 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
                               struct ieee80211_tx_rate *r);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
-#endif
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
 
@@ -822,6 +838,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
                          struct iwl_mvm_phy_ctxt *ctxt);
 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
                            struct iwl_mvm_phy_ctxt *ctxt);
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -848,7 +865,8 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 
 /* Quota management */
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+                         struct ieee80211_vif *disabled_vif);
 
 /* Scanning */
 int iwl_mvm_scan_request(struct iwl_mvm *mvm,
@@ -872,10 +890,19 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
                                       struct cfg80211_sched_scan_request *req);
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                             struct cfg80211_sched_scan_request *req);
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
-                                 struct iwl_rx_cmd_buffer *rxb,
-                                 struct iwl_device_cmd *cmd);
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
+int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd);
+
+/* Unified scan */
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_scan_request *req);
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct cfg80211_sched_scan_request *req,
+                                   struct ieee80211_scan_ies *ies);
 
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -982,6 +1009,24 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac);
 
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+                                struct iwl_rx_cmd_buffer *rxb,
+                                struct iwl_device_cmd *cmd);
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              enum ieee80211_rssi_event rssi_event);
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+                                        struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+                                       enum ieee80211_band band);
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+                                     struct iwl_rx_cmd_buffer *rxb,
+                                     struct iwl_device_cmd *cmd);
+
 enum iwl_bt_kill_msk {
        BT_KILL_MSK_DEFAULT,
        BT_KILL_MSK_SCO_HID_A2DP,
@@ -1053,4 +1098,9 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                      bool added_vif);
 
+/* TDLS */
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
+
 #endif /* __IWL_MVM_H__ */
index 1f1a550828fafef201aa52cdc2f4c1f24aa5b51d..b04805ccb443d1a7b1532a96881a3d739933fd96 100644 (file)
@@ -530,6 +530,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
                        }
 #endif
                }
+               if (!size_read)
+                       IWL_ERR(mvm, "OTP is blank\n");
                kfree(nvm_buffer);
        }
 
index 7bb763f3052b3b39d74d2f07a0589f607cd56fe0..7d7b2fbe7cd1b3906c2378059b99776ccaddf65a 100644 (file)
@@ -166,8 +166,15 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
        WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
                 ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
 
-       /* silicon bits */
-       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
+       /*
+        * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and
+        * shouldn't be set to any non-zero value. The same is supposed to be
+        * true of the other HW, but unsetting them (such as the 7260) causes
+        * automatic tests to fail on seemingly unrelated errors. Need to
+        * further investigate this, but for now we'll separate cases.
+        */
+       if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
 
        iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
                                CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
@@ -233,7 +240,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
        RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
                   iwl_mvm_rx_scan_offload_complete_notif, true),
-       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
+       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
                   false),
 
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
@@ -284,6 +291,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(SCAN_OFFLOAD_ABORT_CMD),
        CMD(SCAN_OFFLOAD_COMPLETE),
        CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+       CMD(SCAN_ITERATION_COMPLETE),
        CMD(POWER_TABLE_CMD),
        CMD(WEP_KEY),
        CMD(REPLY_RX_PHY_CMD),
@@ -324,6 +332,9 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
        CMD(BT_COEX_CI),
+       CMD(BT_COEX_UPDATE_SW_BOOST),
+       CMD(BT_COEX_UPDATE_CORUN_LUT),
+       CMD(BT_COEX_UPDATE_REDUCED_TXP),
        CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
        CMD(ANTENNA_COUPLING_NOTIFICATION),
 };
@@ -502,10 +513,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                }
        }
 
-       scan_size = sizeof(struct iwl_scan_cmd) +
-               mvm->fw->ucode_capa.max_probe_length +
-               (mvm->fw->ucode_capa.n_scan_channels *
-                                       sizeof(struct iwl_scan_channel));
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
+                       sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                       sizeof(struct iwl_scan_probe_req);
+       else
+               scan_size = sizeof(struct iwl_scan_cmd) +
+                       mvm->fw->ucode_capa.max_probe_length +
+                       mvm->fw->ucode_capa.n_scan_channels *
+                               sizeof(struct iwl_scan_channel);
+
        mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
        if (!mvm->scan_cmd)
                goto out_free;
@@ -550,8 +568,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
 
        kfree(mvm->scan_cmd);
        vfree(mvm->fw_error_dump);
-       kfree(mvm->fw_error_sram);
-       kfree(mvm->fw_error_rxf);
        kfree(mvm->mcast_filter_cmd);
        mvm->mcast_filter_cmd = NULL;
 
@@ -755,7 +771,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
        module_put(THIS_MODULE);
 }
 
-static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
 {
        iwl_abort_notification_waits(&mvm->notif_wait);
 
@@ -812,111 +828,24 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                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) {
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
+                  (!fw_error || mvm->restart_fw)) {
                /* don't let the transport/FW power down */
                iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
-               if (mvm->restart_fw > 0)
+               if (fw_error && mvm->restart_fw > 0)
                        mvm->restart_fw--;
                ieee80211_restart_hw(mvm->hw);
        }
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
-{
-       struct iwl_fw_error_dump_file *dump_file;
-       struct iwl_fw_error_dump_data *dump_data;
-       struct iwl_fw_error_dump_info *dump_info;
-       u32 file_len;
-       u32 trans_len;
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (mvm->fw_error_dump)
-               return;
-
-       file_len = sizeof(*dump_file) +
-                  sizeof(*dump_data) * 3 +
-                  mvm->fw_error_sram_len +
-                  mvm->fw_error_rxf_len +
-                  sizeof(*dump_info);
-
-       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
-       if (trans_len)
-               file_len += trans_len;
-
-       dump_file = vmalloc(file_len);
-       if (!dump_file)
-               return;
-
-       mvm->fw_error_dump = dump_file;
-
-       dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
-       dump_file->file_len = cpu_to_le32(file_len);
-       dump_data = (void *)dump_file->data;
-
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
-       dump_data->len = cpu_to_le32(sizeof(*dump_info));
-       dump_info = (void *) dump_data->data;
-       dump_info->device_family =
-               mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
-                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
-                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
-       memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
-              sizeof(dump_info->fw_human_readable));
-       strncpy(dump_info->dev_human_readable, mvm->cfg->name,
-               sizeof(dump_info->dev_human_readable));
-       strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
-               sizeof(dump_info->bus_human_readable));
-
-       dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
-       dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
-       memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
-
-       dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
-       dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
-
-       /*
-        * No need for lock since at the stage the FW isn't loaded. So it
-        * can't assert - we are the only one who can possibly be accessing
-        * mvm->fw_error_sram right now.
-        */
-       memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
-
-       kfree(mvm->fw_error_rxf);
-       mvm->fw_error_rxf = NULL;
-       mvm->fw_error_rxf_len = 0;
-
-       kfree(mvm->fw_error_sram);
-       mvm->fw_error_sram = NULL;
-       mvm->fw_error_sram_len = 0;
-
-       if (trans_len) {
-               void *buf = iwl_fw_error_next_data(dump_data);
-               u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
-                                                        trans_len);
-               dump_data = (void *)((u8 *)buf + real_trans_len);
-               dump_file->file_len =
-                       cpu_to_le32(file_len - trans_len + real_trans_len);
-       }
-}
-#endif
-
 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);
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       iwl_mvm_fw_error_sram_dump(mvm);
-       iwl_mvm_fw_error_rxf_dump(mvm);
-#endif
-
-       iwl_mvm_nic_restart(mvm);
+       iwl_mvm_nic_restart(mvm, true);
 }
 
 static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
@@ -924,7 +853,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
        WARN_ON(1);
-       iwl_mvm_nic_restart(mvm);
+       iwl_mvm_nic_restart(mvm, true);
 }
 
 struct iwl_d0i3_iter_data {
index 539f3a942d437565ab6ba9accd06f71874985af4..6cc243f7cf602b8f926baa11d4a59c30db965e0c 100644 (file)
@@ -261,3 +261,29 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 
        ctxt->ref--;
 }
+
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       unsigned long *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION ||
+           vif->type == NL80211_IFTYPE_AP)
+               __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
+{
+       unsigned long phy_ctxt_counter = 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_binding_iterator,
+                                                  &phy_ctxt_counter);
+
+       return hweight8(phy_ctxt_counter);
+}
index c182a8baf685857d3c2857443d53ae978e8646a8..2b2d10800a55e1b90f3f4da4c91bd6cfe09629a8 100644 (file)
@@ -246,30 +246,10 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
                IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
 }
 
-static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
-                                     struct ieee80211_vif *vif)
-{
-       unsigned long *data = _data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       if (!mvmvif->phy_ctxt)
-               return;
-
-       if (vif->type == NL80211_IFTYPE_STATION ||
-           vif->type == NL80211_IFTYPE_AP)
-               __set_bit(mvmvif->phy_ctxt->id, data);
-}
-
 static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       unsigned long phy_ctxt_counter = 0;
-
-       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  iwl_mvm_binding_iterator,
-                                                  &phy_ctxt_counter);
 
        if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
                    ETH_ALEN))
@@ -291,7 +271,7 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
         * Avoid using uAPSD if client is in DCM -
         * low latency issue in Miracast
         */
-       if (hweight8(phy_ctxt_counter) >= 2)
+       if (iwl_mvm_phy_ctx_count(mvm) >= 2)
                return false;
 
        return true;
@@ -503,6 +483,7 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
 }
 
 struct iwl_power_vifs {
+       struct iwl_mvm *mvm;
        struct ieee80211_vif *bf_vif;
        struct ieee80211_vif *bss_vif;
        struct ieee80211_vif *p2p_vif;
@@ -512,6 +493,8 @@ struct iwl_power_vifs {
        bool bss_active;
        bool ap_active;
        bool monitor_active;
+       bool bss_tdls;
+       bool p2p_tdls;
 };
 
 static void iwl_mvm_power_iterator(void *_data, u8 *mac,
@@ -548,6 +531,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                /* only a single MAC of the same type */
                WARN_ON(power_iterator->p2p_vif);
                power_iterator->p2p_vif = vif;
+               power_iterator->p2p_tdls =
+                       !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
                if (mvmvif->phy_ctxt)
                        if (mvmvif->phy_ctxt->id < MAX_PHYS)
                                power_iterator->p2p_active = true;
@@ -557,6 +542,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                /* only a single MAC of the same type */
                WARN_ON(power_iterator->bss_vif);
                power_iterator->bss_vif = vif;
+               power_iterator->bss_tdls =
+                       !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
                if (mvmvif->phy_ctxt)
                        if (mvmvif->phy_ctxt->id < MAX_PHYS)
                                power_iterator->bss_active = true;
@@ -599,13 +586,15 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
                ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
 
        /* enable PM on bss if bss stand alone */
-       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active &&
+           !vifs->bss_tdls) {
                bss_mvmvif->pm_enabled = true;
                return;
        }
 
        /* enable PM on p2p if p2p stand alone */
-       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active &&
+           !vifs->p2p_tdls) {
                if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
                        p2p_mvmvif->pm_enabled = true;
                return;
@@ -831,7 +820,9 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
 int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
 {
        struct iwl_mvm_vif *mvmvif;
-       struct iwl_power_vifs vifs = {};
+       struct iwl_power_vifs vifs = {
+               .mvm = mvm,
+       };
        bool ba_enable;
        int ret;
 
index ba68d7b8450508d9c7b123500b654d2195b5613a..4e20b3ce2b6a320e7fd163fe34a02674788645fc 100644 (file)
@@ -73,7 +73,7 @@ struct iwl_mvm_quota_iterator_data {
        int colors[MAX_BINDINGS];
        int low_latency[MAX_BINDINGS];
        int n_low_latency_bindings;
-       struct ieee80211_vif *new_vif;
+       struct ieee80211_vif *disabled_vif;
 };
 
 static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
@@ -83,13 +83,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        u16 id;
 
-       /*
-        * We'll account for the new interface (if any) below,
-        * skip it here in case we're not called from within
-        * the add_interface callback (otherwise it won't show
-        * up in iteration)
-        */
-       if (vif == data->new_vif)
+       /* skip disabled interfaces here immediately */
+       if (vif == data->disabled_vif)
                return;
 
        if (!mvmvif->phy_ctxt)
@@ -104,11 +99,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        if (WARN_ON_ONCE(id >= MAX_BINDINGS))
                return;
 
-       if (data->colors[id] < 0)
-               data->colors[id] = mvmvif->phy_ctxt->color;
-       else
-               WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
-
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                if (vif->bss_conf.assoc)
@@ -130,6 +120,11 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                return;
        }
 
+       if (data->colors[id] < 0)
+               data->colors[id] = mvmvif->phy_ctxt->color;
+       else
+               WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
        data->n_interfaces[id]++;
 
        if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
@@ -171,14 +166,15 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
 #endif
 }
 
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+                         struct ieee80211_vif *disabled_vif)
 {
        struct iwl_time_quota_cmd cmd = {};
        int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
        struct iwl_mvm_quota_iterator_data data = {
                .n_interfaces = {},
                .colors = { -1, -1, -1, -1 },
-               .new_vif = newvif,
+               .disabled_vif = disabled_vif,
        };
 
        lockdep_assert_held(&mvm->mutex);
@@ -193,10 +189,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
        ieee80211_iterate_active_interfaces_atomic(
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                iwl_mvm_quota_iterator, &data);
-       if (newvif) {
-               data.new_vif = NULL;
-               iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
-       }
 
        /*
         * The FW's scheduling session consists of
@@ -285,6 +277,14 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
+       /* check that we have non-zero quota for all valid bindings */
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID))
+                       continue;
+               WARN_ONCE(cmd.quotas[i].quota == 0,
+                         "zero quota on binding %d\n", i);
+       }
+
        ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret)
index 306a6caa486889b4dc27ea53fc350f246a7433f8..c70e959bf0e3d443b17ca8e055ee3dd57fda30a2 100644 (file)
@@ -927,7 +927,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
        u8 low;
        u16 high_low;
        u16 rate_mask;
-       struct iwl_mvm *mvm = lq_sta->drv;
+       struct iwl_mvm *mvm = lq_sta->pers.drv;
 
        rate_mask = rs_get_supported_rates(lq_sta, rate);
        high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
@@ -946,7 +946,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
 static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
                                          struct rs_rate *rate)
 {
-       struct iwl_mvm *mvm = lq_sta->drv;
+       struct iwl_mvm *mvm = lq_sta->pers.drv;
 
        if (is_legacy(rate)) {
                /* No column to downgrade from Legacy */
@@ -1026,14 +1026,14 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        if (!lq_sta) {
                IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
                return;
-       } else if (!lq_sta->drv) {
+       } else if (!lq_sta->pers.drv) {
                IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
                return;
        }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        /* Disable last tx check if we are debugging with fixed rate */
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
                return;
        }
@@ -1405,7 +1405,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
        int flush_interval_passed = 0;
        struct iwl_mvm *mvm;
 
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.drv;
        active_tbl = lq_sta->active_tbl;
 
        tbl = &(lq_sta->lq_info[active_tbl]);
@@ -1865,11 +1865,11 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm,
        int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-       if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+       if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
                IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
-                              lq_sta->dbg_fixed_txp_reduction);
-               lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
-               return cur != lq_sta->dbg_fixed_txp_reduction;
+                              lq_sta->pers.dbg_fixed_txp_reduction);
+               lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction;
+               return cur != lq_sta->pers.dbg_fixed_txp_reduction;
        }
 #endif
 
@@ -2382,7 +2382,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        }
 
        /* Treat uninitialized rate scaling data same as non-existing. */
-       if (lq_sta && !lq_sta->drv) {
+       if (lq_sta && !lq_sta->pers.drv) {
                IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
                mvm_sta = NULL;
        }
@@ -2401,12 +2401,18 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
                          gfp_t gfp)
 {
        struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
-       struct iwl_op_mode *op_mode __maybe_unused =
-                       (struct iwl_op_mode *)mvm_rate;
-       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+       struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
+       struct iwl_mvm *mvm  = IWL_OP_MODE_GET_MVM(op_mode);
+       struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
 
        IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
 
+       lq_sta->pers.drv = mvm;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       lq_sta->pers.dbg_fixed_rate = 0;
+       lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
+#endif
+
        return &sta_priv->lq_sta;
 }
 
@@ -2552,7 +2558,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
        lq_sta = &sta_priv->lq_sta;
-       memset(lq_sta, 0, sizeof(*lq_sta));
+
+       /* clear all non-persistent lq data */
+       memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
 
        sband = hw->wiphy->bands[band];
 
@@ -2630,17 +2638,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        /* as default allow aggregation for all tids */
        lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
-       lq_sta->drv = mvm;
 
        /* Set last_txrate_idx to lowest rate */
        lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
        if (sband->band == IEEE80211_BAND_5GHZ)
                lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
        lq_sta->is_agg = 0;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       lq_sta->dbg_fixed_rate = 0;
-       lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
-#endif
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
 #endif
@@ -2811,12 +2814,12 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
        u8 ant = initial_rate->ant;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                rs_build_rates_table_from_fixed(mvm, lq_cmd,
                                                lq_sta->band,
-                                               lq_sta->dbg_fixed_rate);
+                                               lq_sta->pers.dbg_fixed_rate);
                lq_cmd->reduced_tpc = 0;
-               ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
+               ant = (lq_sta->pers.dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
                        RATE_MCS_ANT_POS;
        } else
 #endif
@@ -2926,14 +2929,14 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
        lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
 
        IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
-                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+                      lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate);
 
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                struct rs_rate rate;
-               rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
+               rs_rate_from_ucode_rate(lq_sta->pers.dbg_fixed_rate,
                                        lq_sta->band, &rate);
                rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate);
-               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
+               iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
        }
 }
 
@@ -2946,16 +2949,16 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
        size_t buf_size;
        u32 parsed_rate;
 
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.drv;
        memset(buf, 0, sizeof(buf));
        buf_size = min(count, sizeof(buf) -  1);
        if (copy_from_user(buf, user_buf, buf_size))
                return -EFAULT;
 
        if (sscanf(buf, "%x", &parsed_rate) == 1)
-               lq_sta->dbg_fixed_rate = parsed_rate;
+               lq_sta->pers.dbg_fixed_rate = parsed_rate;
        else
-               lq_sta->dbg_fixed_rate = 0;
+               lq_sta->pers.dbg_fixed_rate = 0;
 
        rs_program_fix_rate(mvm, lq_sta);
 
@@ -2974,7 +2977,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
        struct iwl_mvm *mvm;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct rs_rate *rate = &tbl->rate;
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.drv;
        buff = kmalloc(2048, GFP_KERNEL);
        if (!buff)
                return -ENOMEM;
@@ -2984,7 +2987,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        lq_sta->total_failed, lq_sta->total_success,
                        lq_sta->active_legacy_rate);
        desc += sprintf(buff+desc, "fixed rate 0x%X\n",
-                       lq_sta->dbg_fixed_rate);
+                       lq_sta->pers.dbg_fixed_rate);
        desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
            (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "",
            (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "",
@@ -3182,31 +3185,20 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
 static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
 {
        struct iwl_lq_sta *lq_sta = mvm_sta;
-       lq_sta->rs_sta_dbgfs_scale_table_file =
-               debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
-                                   lq_sta, &rs_sta_dbgfs_scale_table_ops);
-       lq_sta->rs_sta_dbgfs_stats_table_file =
-               debugfs_create_file("rate_stats_table", S_IRUSR, dir,
-                                   lq_sta, &rs_sta_dbgfs_stats_table_ops);
-       lq_sta->rs_sta_dbgfs_drv_tx_stats_file =
-               debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
-                                   lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
-       lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
-               debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
-                                 &lq_sta->tx_agg_tid_en);
-       lq_sta->rs_sta_dbgfs_reduced_txp_file =
-               debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
-                                 &lq_sta->dbg_fixed_txp_reduction);
+       debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_scale_table_ops);
+       debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
+       debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+                         &lq_sta->tx_agg_tid_en);
+       debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+                         &lq_sta->pers.dbg_fixed_txp_reduction);
 }
 
 static void rs_remove_debugfs(void *mvm, void *mvm_sta)
 {
-       struct iwl_lq_sta *lq_sta = mvm_sta;
-       debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
 }
 #endif
 
index 374a83d7db25a98dd76da34d3fdff9557e48664f..f27b9d687a25e9e1854c6e6a9a04331e8994717e 100644 (file)
@@ -349,16 +349,6 @@ struct iwl_lq_sta {
        struct iwl_lq_cmd lq;
        struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
        u8 tx_agg_tid_en;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct dentry *rs_sta_dbgfs_scale_table_file;
-       struct dentry *rs_sta_dbgfs_stats_table_file;
-       struct dentry *rs_sta_dbgfs_drv_tx_stats_file;
-       struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
-       struct dentry *rs_sta_dbgfs_reduced_txp_file;
-       u32 dbg_fixed_rate;
-       u8 dbg_fixed_txp_reduction;
-#endif
-       struct iwl_mvm *drv;
 
        /* used to be in sta_info */
        int last_txrate_idx;
@@ -369,6 +359,15 @@ struct iwl_lq_sta {
 
        /* tx power reduce for this sta */
        int tpc_reduce;
+
+       /* persistent fields - initialized only once - keep last! */
+       struct {
+#ifdef CONFIG_MAC80211_DEBUGFS
+               u32 dbg_fixed_rate;
+               u8 dbg_fixed_txp_reduction;
+#endif
+               struct iwl_mvm *drv;
+       } pers;
 };
 
 /* Initialize station's rate scaling information after adding station */
index cf7276967acdec6439392c82e44e94de52d453c8..4b98987fc4133f6b7d7c0a21a1d11e57658a35f7 100644 (file)
@@ -258,6 +258,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 
        memset(&rx_status, 0, sizeof(rx_status));
 
+       /*
+        * We have tx blocked stations (with CS bit). If we heard frames from
+        * a blocked station on a new channel we can TX to it again.
+        */
+       if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
+               struct ieee80211_sta *sta;
+
+               rcu_read_lock();
+
+               sta = ieee80211_find_sta(
+                       rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
+               if (sta)
+                       iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+               rcu_read_unlock();
+       }
+
        /*
         * drop the packet if it has failed being decrypted by HW
         */
index f2dde5604a8027a715aa216533fff3ac1bd1c4d6..004b1f5d031429a2798cc1d35464daa6ae59db00 100644 (file)
@@ -97,10 +97,9 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
        return cpu_to_le16(rx_chain);
 }
 
-static inline __le32
-iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
+static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band)
 {
-       if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
+       if (band == IEEE80211_BAND_2GHZ)
                return cpu_to_le32(PHY_BAND_24);
        else
                return cpu_to_le32(PHY_BAND_5);
@@ -130,19 +129,19 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
  * request list, is not copied here, but inserted directly to the probe
  * request.
  */
-static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
-                                   struct cfg80211_scan_request *req,
-                                   int first)
+static void iwl_mvm_scan_fill_ssids(struct iwl_ssid_ie *cmd_ssid,
+                                   struct cfg80211_ssid *ssids,
+                                   int n_ssids, int first)
 {
        int fw_idx, req_idx;
 
-       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first;
+       for (req_idx = n_ssids - 1, fw_idx = 0; req_idx >= first;
             req_idx--, fw_idx++) {
-               cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
-               cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
-               memcpy(cmd->direct_scan[fw_idx].ssid,
-                      req->ssids[req_idx].ssid,
-                      req->ssids[req_idx].ssid_len);
+               cmd_ssid[fw_idx].id = WLAN_EID_SSID;
+               cmd_ssid[fw_idx].len = ssids[req_idx].ssid_len;
+               memcpy(cmd_ssid[fw_idx].ssid,
+                      ssids[req_idx].ssid,
+                      ssids[req_idx].ssid_len);
        }
 }
 
@@ -349,7 +348,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        if (params.passive_fragmented)
                cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
 
-       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
+       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
        cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
                                        MAC_FILTER_IN_BEACON);
 
@@ -376,7 +375,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
-       iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
+       iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->ssids, req->n_ssids,
+                               basic_ssid ? 1 : 0);
 
        cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
                                           TX_CMD_FLG_BT_DIS);
@@ -450,16 +450,27 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        return 0;
 }
 
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
-                                 struct iwl_rx_cmd_buffer *rxb,
-                                 struct iwl_device_cmd *cmd)
+int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_sched_scan_results *notif = (void *)pkt->data;
+       u8 client_bitmap = 0;
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+               struct iwl_sched_scan_results *notif = (void *)pkt->data;
 
-       if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
-               IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
-               ieee80211_sched_scan_results(mvm->hw);
+               client_bitmap = notif->client_bitmap;
+       }
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+           client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
+               if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
+                       IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+                       ieee80211_sched_scan_results(mvm->hw);
+               } else {
+                       IWL_DEBUG_SCAN(mvm, "Scan results\n");
+               }
        }
 
        return 0;
@@ -503,7 +514,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
        };
 }
 
-int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
 {
        struct iwl_notification_wait wait_scan_abort;
        static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
@@ -544,26 +555,45 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
                                           struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
+       u8 status, ebs_status;
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
+               struct iwl_periodic_scan_complete *scan_notif;
 
+               scan_notif = (void *)pkt->data;
+               status = scan_notif->status;
+               ebs_status = scan_notif->ebs_status;
+       } else  {
+               struct iwl_scan_offload_complete *scan_notif;
+
+               scan_notif = (void *)pkt->data;
+               status = scan_notif->status;
+               ebs_status = scan_notif->ebs_status;
+       }
        /* scan status must be locked for proper checking */
        lockdep_assert_held(&mvm->mutex);
 
        IWL_DEBUG_SCAN(mvm,
-                      "Scheduled scan completed, status %s EBS status %s:%d\n",
-                      scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
-                      "completed" : "aborted", scan_notif->ebs_status ==
-                      IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
-                      scan_notif->ebs_status);
+                      "%s completed, status %s, EBS status %s\n",
+                      mvm->scan_status == IWL_MVM_SCAN_SCHED ?
+                               "Scheduled scan" : "Scan",
+                      status == IWL_SCAN_OFFLOAD_COMPLETED ?
+                               "completed" : "aborted",
+                      ebs_status == IWL_SCAN_EBS_SUCCESS ?
+                               "success" : "failed");
 
 
        /* only call mac80211 completion if the stop was initiated by FW */
        if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
                mvm->scan_status = IWL_MVM_SCAN_NONE;
                ieee80211_sched_scan_stopped(mvm->hw);
+       } else if (mvm->scan_status == IWL_MVM_SCAN_OS) {
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ieee80211_scan_completed(mvm->hw,
+                                        status == IWL_SCAN_OFFLOAD_ABORTED);
        }
 
-       mvm->last_ebs_successful = !scan_notif->ebs_status;
+       mvm->last_ebs_successful = !ebs_status;
 
        return 0;
 }
@@ -631,8 +661,8 @@ static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
 }
 
 static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
-                                       struct iwl_scan_offload_cmd *scan,
-                                       u32 *ssid_bitmap)
+                                       struct iwl_ssid_ie *direct_scan,
+                                       u32 *ssid_bitmap, bool basic_ssid)
 {
        int i, j;
        int index;
@@ -646,10 +676,10 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
                /* skip empty SSID matchsets */
                if (!req->match_sets[i].ssid.ssid_len)
                        continue;
-               scan->direct_scan[i].id = WLAN_EID_SSID;
-               scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
-               memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
-                      scan->direct_scan[i].len);
+               direct_scan[i].id = WLAN_EID_SSID;
+               direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
+               memcpy(direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
+                      direct_scan[i].len);
        }
 
        /* add SSIDs from scan SSID list */
@@ -657,14 +687,14 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
        for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) {
                index = iwl_ssid_exist(req->ssids[j].ssid,
                                       req->ssids[j].ssid_len,
-                                      scan->direct_scan);
+                                      direct_scan);
                if (index < 0) {
-                       if (!req->ssids[j].ssid_len)
+                       if (!req->ssids[j].ssid_len && basic_ssid)
                                continue;
-                       scan->direct_scan[i].id = WLAN_EID_SSID;
-                       scan->direct_scan[i].len = req->ssids[j].ssid_len;
-                       memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid,
-                              scan->direct_scan[i].len);
+                       direct_scan[i].id = WLAN_EID_SSID;
+                       direct_scan[i].len = req->ssids[j].ssid_len;
+                       memcpy(direct_scan[i].ssid, req->ssids[j].ssid,
+                              direct_scan[i].len);
                        *ssid_bitmap |= BIT(i + 1);
                        i++;
                } else {
@@ -734,6 +764,8 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        int cmd_len;
        int ret;
        u8 *probes;
+       bool basic_ssid = !(mvm->fw->ucode_capa.flags &
+                           IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
 
        struct iwl_scan_offload_cfg *scan_cfg;
        struct iwl_host_cmd cmd = {
@@ -758,7 +790,8 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, &params);
        scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
 
-       iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
+       iwl_scan_offload_build_ssid(req, scan_cfg->scan_cmd.direct_scan,
+                                   &ssid_bitmap, basic_ssid);
        /* build tx frames for supported bands */
        if (band_2ghz) {
                iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
@@ -866,11 +899,11 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                .watchdog = IWL_SCHED_SCAN_WATCHDOG,
 
                .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
-               .schedule_line[0].delay = req->interval / 1000,
+               .schedule_line[0].delay = cpu_to_le16(req->interval / 1000),
                .schedule_line[0].full_scan_mul = 1,
 
                .schedule_line[1].iterations = 0xff,
-               .schedule_line[1].delay = req->interval / 1000,
+               .schedule_line[1].delay = cpu_to_le16(req->interval / 1000),
                .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
        };
 
@@ -893,7 +926,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                                    sizeof(scan_req), &scan_req);
 }
 
-static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
+static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm)
 {
        int ret;
        struct iwl_host_cmd cmd = {
@@ -904,7 +937,9 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        /* Exit instantly with error when device is not ready
         * to receive scan abort command or it does not perform
         * scheduled scan currently */
-       if (mvm->scan_status != IWL_MVM_SCAN_SCHED)
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+           (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+            mvm->scan_status != IWL_MVM_SCAN_OS))
                return -EIO;
 
        ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
@@ -926,16 +961,19 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify)
 {
        int ret;
        struct iwl_notification_wait wait_scan_done;
        static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
+       bool sched = mvm->scan_status == IWL_MVM_SCAN_SCHED;
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
-               IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+           (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+            mvm->scan_status != IWL_MVM_SCAN_OS)) {
+               IWL_DEBUG_SCAN(mvm, "No scan to stop\n");
                return 0;
        }
 
@@ -944,14 +982,16 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
                                   ARRAY_SIZE(scan_done_notif),
                                   NULL, NULL);
 
-       ret = iwl_mvm_send_sched_scan_abort(mvm);
+       ret = iwl_mvm_send_scan_offload_abort(mvm);
        if (ret) {
-               IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
+               IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n",
+                              sched ? "offloaded " : "", ret);
                iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
                return ret;
        }
 
-       IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+       IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n",
+                      sched ? "offloaded " : "");
 
        ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
        if (ret)
@@ -964,8 +1004,317 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
         */
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
-       if (notify)
-               ieee80211_sched_scan_stopped(mvm->hw);
+       if (notify) {
+               if (sched)
+                       ieee80211_sched_scan_stopped(mvm->hw);
+               else
+                       ieee80211_scan_completed(mvm->hw, true);
+       }
 
        return 0;
 }
+
+static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm,
+                                            struct iwl_scan_req_tx_cmd *tx_cmd,
+                                            bool no_cck)
+{
+       tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                        TX_CMD_FLG_BT_DIS);
+       tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+                                                          IEEE80211_BAND_2GHZ,
+                                                          no_cck);
+       tx_cmd[0].sta_id = mvm->aux_sta.sta_id;
+
+       tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                        TX_CMD_FLG_BT_DIS);
+       tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+                                                          IEEE80211_BAND_5GHZ,
+                                                          no_cck);
+       tx_cmd[1].sta_id = mvm->aux_sta.sta_id;
+}
+
+static void
+iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
+                              struct ieee80211_channel **channels,
+                              int n_channels, u32 ssid_bitmap,
+                              struct iwl_scan_req_unified_lmac *cmd)
+{
+       struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data;
+       int i;
+
+       for (i = 0; i < n_channels; i++) {
+               channel_cfg[i].channel_num =
+                       cpu_to_le16(channels[i]->hw_value);
+               channel_cfg[i].iter_count = cpu_to_le16(1);
+               channel_cfg[i].iter_interval = 0;
+               channel_cfg[i].flags =
+                       cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL |
+                                   ssid_bitmap);
+       }
+}
+
+static void
+iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                                struct ieee80211_scan_ies *ies,
+                                struct iwl_scan_req_unified_lmac *cmd)
+{
+       struct iwl_scan_probe_req *preq = (void *)(cmd->data +
+               sizeof(struct iwl_scan_channel_cfg_lmac) *
+                       mvm->fw->ucode_capa.n_scan_channels);
+       struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
+       u8 *pos;
+
+       frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+       eth_broadcast_addr(frame->da);
+       memcpy(frame->sa, vif->addr, ETH_ALEN);
+       eth_broadcast_addr(frame->bssid);
+       frame->seq_ctrl = 0;
+
+       pos = frame->u.probe_req.variable;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = 0;
+
+       preq->mac_header.offset = 0;
+       preq->mac_header.len = cpu_to_le16(24 + 2);
+
+       memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
+              ies->len[IEEE80211_BAND_2GHZ]);
+       preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
+       preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
+       pos += ies->len[IEEE80211_BAND_2GHZ];
+
+       memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
+              ies->len[IEEE80211_BAND_5GHZ]);
+       preq->band_data[1].offset = cpu_to_le16(pos - preq->buf);
+       preq->band_data[1].len = cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]);
+       pos += ies->len[IEEE80211_BAND_5GHZ];
+
+       memcpy(pos, ies->common_ies, ies->common_ie_len);
+       preq->common_data.offset = cpu_to_le16(pos - preq->buf);
+       preq->common_data.len = cpu_to_le16(ies->common_ie_len);
+}
+
+static void
+iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm,
+                                      struct iwl_scan_req_unified_lmac *cmd,
+                                      struct iwl_mvm_scan_params *params)
+{
+       memset(cmd, 0, ksize(cmd));
+       cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active;
+       cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive;
+       /* TODO: Use params; now fragmented isn't used. */
+       cmd->fragmented_dwell = 0;
+       cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
+       cmd->max_out_time = cpu_to_le32(params->max_out_time);
+       cmd->suspend_time = cpu_to_le32(params->suspend_time);
+       cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+       cmd->iter_num = cpu_to_le32(1);
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+           mvm->last_ebs_successful) {
+               cmd->channel_opt[0].flags =
+                       cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+                                   IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+                                   IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+               cmd->channel_opt[1].flags =
+                       cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+                                   IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+                                   IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+       }
+}
+
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_scan_request *req)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_OFFLOAD_REQUEST_CMD,
+               .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+                        sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                        sizeof(struct iwl_scan_probe_req), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_mvm_scan_params params = {};
+       u32 flags;
+       int ssid_bitmap = 0;
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON_ONCE(req->req.n_ssids > PROBE_OPTION_MAX ||
+                        req->ies.common_ie_len + req->ies.len[0] +
+                               req->ies.len[1] + 24 + 2 >
+                                       SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+                        req->req.n_channels >
+                               mvm->fw->ucode_capa.n_scan_channels))
+               return -1;
+
+       mvm->scan_status = IWL_MVM_SCAN_OS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+                                &params);
+
+       iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+       cmd->n_channels = (u8)req->req.n_channels;
+
+       flags = IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+
+       if (req->req.n_ssids == 1 && req->req.ssids[0].ssid_len != 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+       if (params.passive_fragmented)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+       if (req->req.n_ssids == 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+       cmd->scan_flags = cpu_to_le32(flags);
+
+       cmd->flags = iwl_mvm_scan_rxon_flags(req->req.channels[0]->band);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, req->req.no_cck);
+       iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->req.ssids,
+                               req->req.n_ssids, 0);
+
+       cmd->schedule[0].delay = 0;
+       cmd->schedule[0].iterations = 1;
+       cmd->schedule[0].full_scan_mul = 0;
+       cmd->schedule[1].delay = 0;
+       cmd->schedule[1].iterations = 0;
+       cmd->schedule[1].full_scan_mul = 0;
+
+       for (i = 1; i <= req->req.n_ssids; i++)
+               ssid_bitmap |= BIT(i);
+
+       iwl_mvm_lmac_scan_cfg_channels(mvm, req->req.channels,
+                                      req->req.n_channels, ssid_bitmap,
+                                      cmd);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct cfg80211_sched_scan_request *req,
+                                   struct ieee80211_scan_ies *ies)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_OFFLOAD_REQUEST_CMD,
+               .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+                        sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                        sizeof(struct iwl_scan_probe_req), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_mvm_scan_params params = {};
+       int ret;
+       u32 flags = 0, ssid_bitmap = 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON_ONCE(req->n_ssids > PROBE_OPTION_MAX ||
+                        ies->common_ie_len + ies->len[0] + ies->len[1] + 24 + 2
+                               > SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+                        req->n_channels > mvm->fw->ucode_capa.n_scan_channels))
+               return -ENOBUFS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
+
+       iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+       cmd->n_channels = (u8)req->n_channels;
+
+       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending scheduled scan with filtering, n_match_sets %d\n",
+                              req->n_match_sets);
+       } else {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending Scheduled scan without filtering\n");
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+       }
+
+       if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+       if (params.passive_fragmented)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+       if (req->n_ssids == 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+       cmd->scan_flags = cpu_to_le32(flags);
+
+       cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, false);
+       iwl_scan_offload_build_ssid(req, cmd->direct_scan, &ssid_bitmap, false);
+
+       cmd->schedule[0].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+       cmd->schedule[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS;
+       cmd->schedule[0].full_scan_mul = 1;
+
+       cmd->schedule[1].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+       cmd->schedule[1].iterations = 0xff;
+       cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER;
+
+       iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+                                      ssid_bitmap, cmd);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sched scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+
+int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+{
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               return iwl_mvm_scan_offload_stop(mvm, true);
+       return iwl_mvm_cancel_regular_scan(mvm);
+}
index d3a6cf7558eb5d4351937da3a9427a296509cc04..81281396484724a9fc6cab21bb59b91b12ad52c1 100644 (file)
@@ -1468,3 +1468,57 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
+
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta,
+                                     bool disable)
+{
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+       spin_lock_bh(&mvm_sta->lock);
+
+       if (mvm_sta->disable_tx == disable) {
+               spin_unlock_bh(&mvm_sta->lock);
+               return;
+       }
+
+       mvm_sta->disable_tx = disable;
+
+       /*
+        * Tell mac80211 to start/stop queueing tx for this station,
+        * but don't stop queueing if there are still pending frames
+        * for this station.
+        */
+       if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
+               ieee80211_sta_block_awake(mvm->hw, sta, disable);
+
+       iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
+
+       spin_unlock_bh(&mvm_sta->lock);
+}
+
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+                                      struct iwl_mvm_vif *mvmvif,
+                                      bool disable)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvm_sta;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Block/unblock all the stations of the given mvmvif */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta))
+                       continue;
+
+               mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+               if (mvm_sta->mac_id_n_color !=
+                   FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
+                       continue;
+
+               iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
+       }
+}
index 10c1a53526518cf2eea9359641fdeb044070a24f..3b1c8bd6cb54356c41c102402657cf765f617a50 100644 (file)
@@ -73,6 +73,7 @@
 #include "rs.h"
 
 struct iwl_mvm;
+struct iwl_mvm_vif;
 
 /**
  * DOC: station table - introduction
@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
  * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
  * @tx_protection: reference counter for controlling the Tx protection.
  * @tt_tx_protection: is thermal throttling enable Tx protection?
+ * @disable_tx: is tx to this STA disabled?
  *
  * When mac80211 creates a station it reserves some space (hw->sta_data_size)
  * in the structure for use by driver. This structure is placed in that
@@ -317,6 +319,8 @@ struct iwl_mvm_sta {
        /* Temporary, until the new TLC will control the Tx protection */
        s8 tx_protection;
        bool tt_tx_protection;
+
+       bool disable_tx;
 };
 
 static inline struct iwl_mvm_sta *
@@ -406,5 +410,11 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain);
 void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
                                   struct iwl_mvm_sta *mvmsta, bool disable);
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta,
+                                     bool disable);
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+                                      struct iwl_mvm_vif *mvmvif,
+                                      bool disable);
 
 #endif /* __sta_h__ */
index 80100f6cc12a85a79fc71bd83cfe2f6962f8f1c0..ae52613b97f2da706a72bae3d04ce9015e8495c8 100644 (file)
@@ -138,6 +138,41 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
        schedule_work(&mvm->roc_done_wk);
 }
 
+static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
+{
+       struct ieee80211_vif *csa_vif;
+
+       rcu_read_lock();
+
+       csa_vif = rcu_dereference(mvm->csa_vif);
+       if (!csa_vif || !csa_vif->csa_active)
+               goto out_unlock;
+
+       IWL_DEBUG_TE(mvm, "CSA NOA started\n");
+
+       /*
+        * CSA NoA is started but we still have beacons to
+        * transmit on the current channel.
+        * So we just do nothing here and the switch
+        * will be performed on the last TBTT.
+        */
+       if (!ieee80211_csa_is_complete(csa_vif)) {
+               IWL_WARN(mvm, "CSA NOA started too early\n");
+               goto out_unlock;
+       }
+
+       ieee80211_csa_finish(csa_vif);
+
+       rcu_read_unlock();
+
+       RCU_INIT_POINTER(mvm->csa_vif, NULL);
+
+       return;
+
+out_unlock:
+       rcu_read_unlock();
+}
+
 static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif,
                                        const char *errmsg)
@@ -213,6 +248,14 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                        set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
                        iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
                        ieee80211_ready_on_channel(mvm->hw);
+               } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
+                       if (le32_to_cpu(notif->status))
+                               iwl_mvm_csa_noa_start(mvm);
+                       else
+                               IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
+
+                       /* we don't need it anymore */
+                       iwl_mvm_te_clear_data(mvm, te_data);
                }
        } else {
                IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -538,3 +581,33 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
 
        iwl_mvm_roc_finished(mvm);
 }
+
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             u32 duration, u32 apply_time)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       struct iwl_time_event_cmd time_cmd = {};
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (te_data->running) {
+               IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n");
+               return -EBUSY;
+       }
+
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       time_cmd.id = cpu_to_le32(TE_P2P_GO_CSA_NOA);
+       time_cmd.apply_time = cpu_to_le32(apply_time);
+       time_cmd.max_frags = TE_V2_FRAG_NONE;
+       time_cmd.duration = cpu_to_le32(duration);
+       time_cmd.repeat = 1;
+       time_cmd.interval = cpu_to_le32(1);
+       time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+                                     TE_V2_ABSENCE);
+
+       return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+}
index 4a61c8c02372824cd08b3901f2e26c2a0353f35d..2f48a90d4ad3e61b010609e2c1d13357c92b3427 100644 (file)
@@ -214,4 +214,33 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
 
 void iwl_mvm_roc_done_wk(struct work_struct *wk);
 
+/**
+ * iwl_mvm_schedule_csa_noa - request NoA for channel switch
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the channel switch is issued
+ * @duration: the duration of the NoA in TU.
+ * @apply_time: NoA start time in GP2.
+ *
+ * This function is used to schedule NoA time event and is used to perform
+ * the channel switch flow.
+ */
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 apply_time);
+
+/**
+ * iwl_mvm_te_scheduled - check if the fw received the TE cmd
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function returns true iff this TE is added to the fw.
+ */
+static inline bool
+iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)
+{
+       if (!te_data)
+               return false;
+
+       return !!te_data->uid;
+}
+
 #endif /* __time_event_h__ */
index 4f7cff506ee7eec85ab70fd972ddd36712ba8b0f..e9ff38635c21914fa8d4174c0582df61cf1c4779 100644 (file)
@@ -131,7 +131,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
            !is_multicast_ether_addr(ieee80211_get_DA(hdr)))
                tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
 
-       tx_cmd->driver_txop = 0;
        tx_cmd->tx_flags = cpu_to_le32(tx_flags);
        /* Total # bytes to be transmitted */
        tx_cmd->len = cpu_to_le16((u16)skb->len);
@@ -723,18 +722,26 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
        /* We can't free more than one frame at once on a shared queue */
        WARN_ON(skb_freed > 1);
 
-       /* If we have still frames from this STA nothing to do here */
+       /* If we have still frames for this STA nothing to do here */
        if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
                goto out;
 
        if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+
                /*
-                * If there are no pending frames for this STA, notify
-                * mac80211 that this station can go to sleep in its
+                * If there are no pending frames for this STA and
+                * the tx to this station is not disabled, notify
+                * mac80211 that this station can now wake up in its
                 * STA table.
                 * If mvmsta is not NULL, sta is valid.
                 */
-               ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+               spin_lock_bh(&mvmsta->lock);
+
+               if (!mvmsta->disable_tx)
+                       ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+               spin_unlock_bh(&mvmsta->lock);
        }
 
        if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
index aa9fc77e8413b607861e370169e0b55ba4b697d1..ac249da8a22b0840ce8de74638ba1969a119c67c 100644 (file)
@@ -519,71 +519,6 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                iwl_mvm_dump_umac_error_log(mvm);
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
-{
-       const struct fw_img *img;
-       u32 ofs, sram_len;
-       void *sram;
-
-       if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)
-               return;
-
-       img = &mvm->fw->img[mvm->cur_ucode];
-       ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
-       sram = kzalloc(sram_len, GFP_ATOMIC);
-       if (!sram)
-               return;
-
-       iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len);
-       mvm->fw_error_sram = sram;
-       mvm->fw_error_sram_len = sram_len;
-}
-
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
-{
-       int i, reg_val;
-       unsigned long flags;
-
-       if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump)
-               return;
-
-       /* reading buffer size */
-       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
-       mvm->fw_error_rxf_len =
-               (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
-
-       /* the register holds the value divided by 128 */
-       mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7;
-
-       if (!mvm->fw_error_rxf_len)
-               return;
-
-       mvm->fw_error_rxf =  kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC);
-       if (!mvm->fw_error_rxf) {
-               mvm->fw_error_rxf_len = 0;
-               return;
-       }
-
-       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
-               kfree(mvm->fw_error_rxf);
-               mvm->fw_error_rxf = NULL;
-               mvm->fw_error_rxf_len = 0;
-               return;
-       }
-
-       for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) {
-               iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR,
-                                    i * sizeof(u32));
-               mvm->fw_error_rxf[i] =
-                       iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR);
-       }
-       iwl_trans_release_nic_access(mvm->trans, &flags);
-}
-#endif
-
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
index 7091a18d5a72f9880f7a47f7a949fefd9de3b9f1..98950e45c7b01e2babd58cf674feff2054e53c3a 100644 (file)
@@ -367,6 +367,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
@@ -380,7 +381,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x095A, 0x9200, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
index 5703a3d7799b380937152ae5fadeceab853f42ef..5b5b0d8c6f6051f7dd0311cf1437090d36a1d2f2 100644 (file)
@@ -1787,7 +1787,7 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
                cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
 
        if (trans_pcie->fw_mon_page)
-               len += sizeof(*data) + sizeof(struct iwl_fw_error_fw_mon) +
+               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
                        trans_pcie->fw_mon_size;
 
        if (!buf)
@@ -1822,7 +1822,7 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
        len += sizeof(*data);
 
        if (trans_pcie->fw_mon_page) {
-               struct iwl_fw_error_fw_mon *fw_mon_data;
+               struct iwl_fw_error_dump_fw_mon *fw_mon_data;
 
                data = iwl_fw_error_next_data(data);
                data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
index 9d6d8d9f01e39bfe2716e28fed9b4d7b7e61b140..62f5dbe602d3de46f8daf6408f776670e024fe05 100644 (file)
@@ -541,7 +541,6 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
 int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
 {
        struct host_cmd_ds_11n_addba_req add_ba_req;
-       struct mwifiex_sta_node *sta_ptr;
        u32 tx_win_size = priv->add_ba_param.tx_win_size;
        static u8 dialog_tok;
        int ret;
@@ -553,6 +552,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
            ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
            priv->adapter->is_hw_11ac_capable &&
            memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
+               struct mwifiex_sta_node *sta_ptr;
+
                sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
                if (!sta_ptr) {
                        dev_warn(priv->adapter->dev,
index b4c14b0fd3cbd0deb7155e8e993fe0f87636724d..8720a3d3c755c6065dd9db413ee708680647b531 100644 (file)
@@ -185,6 +185,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
        tx_info_aggr =  MWIFIEX_SKB_TXCB(skb_aggr);
 
+       memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
        tx_info_aggr->bss_type = tx_info_src->bss_type;
        tx_info_aggr->bss_num = tx_info_src->bss_num;
 
index b22bae3d1205bd0c22000fc9e66a50795e1f3eb9..06a2c215ef5e13129a53e72add714c05cbb77489 100644 (file)
@@ -249,13 +249,22 @@ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
  * buffered in Rx reordering table.
  */
 static int
-mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr)
+mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
 {
+       struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
+       struct mwifiex_private *priv = ctx->priv;
+       unsigned long flags;
        int i;
 
-       for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i)
-               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
+               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        return i;
+               }
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        return -1;
 }
@@ -274,7 +283,7 @@ mwifiex_flush_data(unsigned long context)
                (struct reorder_tmr_cnxt *) context;
        int start_win, seq_num;
 
-       seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr);
+       seq_num = mwifiex_11n_find_last_seq_num(ctx);
 
        if (seq_num < 0)
                return;
@@ -729,9 +738,9 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
                mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
                spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        }
+       INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
-       INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
        mwifiex_reset_11n_rx_seq_num(priv);
 }
 
@@ -749,10 +758,14 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
                priv = adapter->priv[i];
                if (!priv)
                        continue;
-               if (list_empty(&priv->rx_reorder_tbl_ptr))
-                       continue;
 
                spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags);
+               if (list_empty(&priv->rx_reorder_tbl_ptr)) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              lock_flags);
+                       continue;
+               }
+
                list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
                        tbl->flags = flags;
                spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags);
index 15fa7b453372e76af91802c3efb295a57ded50b7..a738cc959a478cdf4f2cab7a061c9f3688202156 100644 (file)
@@ -188,6 +188,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        }
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
        tx_info->pkt_len = pkt_len;
@@ -1603,9 +1604,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
                return -EINVAL;
        }
 
-       /* disconnect before try to associate */
-       mwifiex_deauthenticate(priv, NULL);
-
        /* As this is new association, clear locally stored
         * keys and security related flags */
        priv->sec_info.wpa_enabled = false;
@@ -1743,6 +1741,11 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
 
+       if (priv->wdev && priv->wdev->current_bss) {
+               wiphy_warn(wiphy, "%s: already connected\n", dev->name);
+               return -EALREADY;
+       }
+
        wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
                  (char *) sme->ssid, sme->bssid);
 
index df42f066d70cfedb6180cda31083b37d5c9c19e8..baf0aab63c04d17bb1d01501e0c29f524b4a5f57 100644 (file)
@@ -137,7 +137,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
        struct host_cmd_ds_command *host_cmd;
        uint16_t cmd_code;
        uint16_t cmd_size;
-       struct timeval tstamp;
        unsigned long flags;
        __le32 tmp;
 
@@ -198,10 +197,8 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
                 */
                skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
-               " seqno %#x\n",
-               tstamp.tv_sec, tstamp.tv_usec, cmd_code,
+       dev_dbg(adapter->dev,
+               "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", cmd_code,
                le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size,
                le16_to_cpu(host_cmd->seq_num));
 
@@ -273,7 +270,6 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
                                (struct mwifiex_opt_sleep_confirm *)
                                                adapter->sleep_cfm->data;
        struct sk_buff *sleep_cfm_tmp;
-       struct timeval ts;
        __le32 tmp;
 
        priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
@@ -284,10 +280,9 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
                                        (adapter->seq_num, priv->bss_num,
                                         priv->bss_type)));
 
-       do_gettimeofday(&ts);
        dev_dbg(adapter->dev,
-               "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d, seqno %#x\n",
-               ts.tv_sec, ts.tv_usec, le16_to_cpu(sleep_cfm_buf->command),
+               "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+               le16_to_cpu(sleep_cfm_buf->command),
                le16_to_cpu(sleep_cfm_buf->action),
                le16_to_cpu(sleep_cfm_buf->size),
                le16_to_cpu(sleep_cfm_buf->seq_num));
@@ -442,7 +437,6 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
                mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
        struct sk_buff *skb = adapter->event_skb;
        u32 eventcause = adapter->event_cause;
-       struct timeval tstamp;
        struct mwifiex_rxinfo *rx_info;
 
        /* Save the last event to debug log */
@@ -462,13 +456,12 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
 
        if (skb) {
                rx_info = MWIFIEX_SKB_RXCB(skb);
+               memset(rx_info, 0, sizeof(*rx_info));
                rx_info->bss_num = priv->bss_num;
                rx_info->bss_type = priv->bss_type;
        }
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "EVENT: %lu.%lu: cause: %#x\n",
-               tstamp.tv_sec, tstamp.tv_usec, eventcause);
+       dev_dbg(adapter->dev, "EVENT: cause: %#x\n", eventcause);
        if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) {
                /* Handle PS_SLEEP/AWAKE events on STA */
                priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
@@ -780,7 +773,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
        uint16_t orig_cmdresp_no;
        uint16_t cmdresp_no;
        uint16_t cmdresp_result;
-       struct timeval tstamp;
        unsigned long flags;
 
        /* Now we got response from FW, cancel the command timer */
@@ -838,11 +830,10 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
        adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
                                                                orig_cmdresp_no;
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d,"
-               " len %d, seqno 0x%x\n",
-              tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result,
-              le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
+       dev_dbg(adapter->dev,
+               "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+               orig_cmdresp_no, cmdresp_result,
+               le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
 
        if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
                dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n");
@@ -902,7 +893,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
        struct mwifiex_adapter *adapter =
                (struct mwifiex_adapter *) function_context;
        struct cmd_ctrl_node *cmd_node;
-       struct timeval tstamp;
 
        adapter->is_cmd_timedout = 1;
        if (!adapter->curr_cmd) {
@@ -915,10 +905,8 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
                        adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
                adapter->dbg.timeout_cmd_act =
                        adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
-               do_gettimeofday(&tstamp);
                dev_err(adapter->dev,
-                       "%s: Timeout cmd id (%lu.%lu) = %#x, act = %#x\n",
-                       __func__, tstamp.tv_sec, tstamp.tv_usec,
+                       "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
                        adapter->dbg.timeout_cmd_id,
                        adapter->dbg.timeout_cmd_act);
 
@@ -1236,18 +1224,15 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
        uint16_t result = le16_to_cpu(cmd->result);
        uint16_t command = le16_to_cpu(cmd->command);
        uint16_t seq_num = le16_to_cpu(cmd->seq_num);
-       struct timeval ts;
 
        if (!upld_len) {
                dev_err(adapter->dev, "%s: cmd size is 0\n", __func__);
                return;
        }
 
-       do_gettimeofday(&ts);
        dev_dbg(adapter->dev,
-               "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d, len %d, seqno 0x%x\n",
-               ts.tv_sec, ts.tv_usec, command, result, le16_to_cpu(cmd->size),
-               seq_num);
+               "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+               command, result, le16_to_cpu(cmd->size), seq_num);
 
        /* Get BSS number and corresponding priv */
        priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
index 5561573452bb4a27cc92a6ae490303448ea0e103..49da2d53d29455830fb9958bc4ab7dbbcd5d5666 100644 (file)
@@ -713,7 +713,7 @@ struct mwifiex_ie_types_vendor_param_set {
        u8 ie[MWIFIEX_MAX_VSIE_LEN];
 };
 
-#define MWIFIEX_TDLS_IDLE_TIMEOUT      60
+#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC       60
 
 struct mwifiex_ie_types_tdls_idle_timeout {
        struct mwifiex_ie_types_header header;
index fc135649b85f4ebc63edfb0b2b2f7c4d9871ef98..8d6c25908b6d012fc9f2ef3d9f9211fd5f09b862 100644 (file)
@@ -949,7 +949,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
                                chan_tlv->chan_scan_param[0].radio_type |=
                                        (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4);
                        else if (adapter->sec_chan_offset ==
-                                           IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
+                                           IEEE80211_HT_PARAM_CHA_SEC_BELOW)
                                chan_tlv->chan_scan_param[0].radio_type |=
                                        (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4);
                }
@@ -1288,8 +1288,6 @@ done:
 int mwifiex_associate(struct mwifiex_private *priv,
                      struct mwifiex_bssdescriptor *bss_desc)
 {
-       u8 current_bssid[ETH_ALEN];
-
        /* Return error if the adapter is not STA role or table entry
         * is not marked as infra.
         */
@@ -1304,10 +1302,6 @@ int mwifiex_associate(struct mwifiex_private *priv,
        else
                mwifiex_set_ba_params(priv);
 
-       memcpy(&current_bssid,
-              &priv->curr_bss_params.bss_descriptor.mac_address,
-              sizeof(current_bssid));
-
        /* Clear any past association response stored for application
           retrieval */
        priv->assoc_rsp_size = 0;
index 657504c3c79dbbdfd0ce666aa76158bd913952ff..dfa37eadc4db566ecc75db890c838d8d04600917 100644 (file)
@@ -33,6 +33,7 @@ static void scan_delay_timer_fn(unsigned long data)
        struct mwifiex_private *priv = (struct mwifiex_private *)data;
        struct mwifiex_adapter *adapter = priv->adapter;
        struct cmd_ctrl_node *cmd_node, *tmp_node;
+       spinlock_t *scan_q_lock = &adapter->scan_pending_q_lock;
        unsigned long flags;
 
        if (adapter->surprise_removed)
@@ -44,13 +45,13 @@ static void scan_delay_timer_fn(unsigned long data)
                 * Abort scan operation by cancelling all pending scan
                 * commands
                 */
-               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+               spin_lock_irqsave(scan_q_lock, flags);
                list_for_each_entry_safe(cmd_node, tmp_node,
                                         &adapter->scan_pending_q, list) {
                        list_del(&cmd_node->list);
                        mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
                }
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+               spin_unlock_irqrestore(scan_q_lock, flags);
 
                spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
                adapter->scan_processing = false;
@@ -79,12 +80,17 @@ static void scan_delay_timer_fn(unsigned long data)
                         */
                        adapter->scan_delay_cnt = 0;
                        adapter->empty_tx_q_cnt = 0;
-                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+                       spin_lock_irqsave(scan_q_lock, flags);
+
+                       if (list_empty(&adapter->scan_pending_q)) {
+                               spin_unlock_irqrestore(scan_q_lock, flags);
+                               goto done;
+                       }
+
                        cmd_node = list_first_entry(&adapter->scan_pending_q,
                                                    struct cmd_ctrl_node, list);
                        list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
+                       spin_unlock_irqrestore(scan_q_lock, flags);
 
                        mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
                                                        true);
@@ -644,6 +650,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
        tx_info->pkt_len = skb->len;
index 5f7afffdd34e73dcdc020555afecd1e84c7a08ed..c16dd2cc81987791d760cf9bdff9aeadcda36579 100644 (file)
@@ -2238,7 +2238,6 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
        struct pcie_service_card *card = adapter->card;
        const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
        unsigned int reg, reg_start, reg_end;
-       struct timeval t;
        u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
        enum rdwr_status stat;
        u32 memory_size;
@@ -2257,9 +2256,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                entry->mem_size = 0;
        }
 
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump start ==\n");
 
        /* Read the number of the memories which will dump */
        stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
@@ -2303,9 +2300,8 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                end_ptr = dbg_ptr + memory_size;
 
                doneflag = entry->done_flag;
-               do_gettimeofday(&t);
-               dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
-                        entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
 
                do {
                        stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
@@ -2331,9 +2327,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                        break;
                } while (true);
        }
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
 
        kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
 
index 1da04a086bd955b3e64fd5556bee8e56284c8898..3e48ef5ca53c6f86b18b4e5ca8e7a77057c4d87d 100644 (file)
@@ -2012,7 +2012,6 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
        int ret = 0;
        unsigned int reg, reg_start, reg_end;
        u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
-       struct timeval t;
        enum rdwr_status stat;
        u32 memory_size;
        static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL };
@@ -2033,9 +2032,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
        mwifiex_pm_wakeup_card(adapter);
        sdio_claim_host(card->func);
 
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump start ==\n");
 
        stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
        if (stat == RDWR_STATUS_FAILURE)
@@ -2087,9 +2084,8 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
                end_ptr = dbg_ptr + memory_size;
 
                doneflag = entry->done_flag;
-               do_gettimeofday(&t);
-               dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
-                        entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
 
                do {
                        stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
@@ -2120,9 +2116,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
                        break;
                } while (1);
        }
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
 
        kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
 
index 0f077aaadab6be43441f3f5be535490e2059b72e..733de92a4c611e39da93fb580a272203d269e54f 100644 (file)
@@ -1647,7 +1647,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
                timeout = (void *)(pos + config_len);
                timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT);
                timeout->header.len = cpu_to_le16(sizeof(timeout->value));
-               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT);
+               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC);
                config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout);
 
                break;
index 822357b7b0bbb04818192457cab3df6e682ffb10..08b78baeb846d65ef18680e462991c226c94d5e3 100644 (file)
@@ -908,7 +908,7 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
                break;
        default:
                dev_err(priv->adapter->dev,
-                       "Unknown TDLS command action respnse %d", action);
+                       "Unknown TDLS command action response %d", action);
                return -1;
        }
 
index 1a03d4d8b418453e52ed3618501c3c64da66db52..caae9738100aa732087e2d03477b063bc1534fe2 100644 (file)
@@ -283,10 +283,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
            priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                u8 config_bands;
 
-               ret = mwifiex_deauthenticate(priv, NULL);
-               if (ret)
-                       goto done;
-
                if (!bss_desc)
                        return -1;
 
@@ -345,12 +341,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                        goto done;
                }
 
-               /* Exit Adhoc mode first */
-               dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n");
-               ret = mwifiex_deauthenticate(priv, NULL);
-               if (ret)
-                       goto done;
-
                priv->adhoc_is_link_sensed = false;
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
index cf330ba951cd86cc5731ef1fa7f29ee3ec4f81ba..dab7b33c54bed0d2849ba2b275b103bf0925ba1e 100644 (file)
@@ -150,6 +150,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
                return -1;
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
        tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN);
index 3efbcbe7e891cc18c5098c84c5216073ee583aed..4c5fd953893dcb22712f60c618f68499351c1dff 100644 (file)
@@ -604,6 +604,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
        }
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
 
@@ -757,6 +758,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
        skb->priority = MWIFIEX_PRIO_VI;
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
        tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
@@ -779,6 +781,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
        struct mwifiex_sta_node *sta_ptr;
        u8 *peer, *pos, *end;
        u8 i, action, basic;
+       __le16 cap = 0;
        int ie_len = 0;
 
        if (len < (sizeof(struct ethhdr) + 3))
@@ -790,18 +793,9 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 
        peer = buf + ETH_ALEN;
        action = *(buf + sizeof(struct ethhdr) + 2);
-
-       /* just handle TDLS setup request/response/confirm */
-       if (action > WLAN_TDLS_SETUP_CONFIRM)
-               return;
-
        dev_dbg(priv->adapter->dev,
                "rx:tdls action: peer=%pM, action=%d\n", peer, action);
 
-       sta_ptr = mwifiex_add_sta_entry(priv, peer);
-       if (!sta_ptr)
-               return;
-
        switch (action) {
        case WLAN_TDLS_SETUP_REQUEST:
                if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN))
@@ -809,7 +803,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 
                pos = buf + sizeof(struct ethhdr) + 4;
                /* payload 1+ category 1 + action 1 + dialog 1 */
-               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               cap = cpu_to_le16(*(u16 *)pos);
                ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN;
                pos += 2;
                break;
@@ -819,7 +813,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                        return;
                /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/
                pos = buf + sizeof(struct ethhdr) + 6;
-               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               cap = cpu_to_le16(*(u16 *)pos);
                ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN;
                pos += 2;
                break;
@@ -831,10 +825,16 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN;
                break;
        default:
-               dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n");
+               dev_dbg(priv->adapter->dev, "Unknown TDLS frame type.\n");
                return;
        }
 
+       sta_ptr = mwifiex_add_sta_entry(priv, peer);
+       if (!sta_ptr)
+               return;
+
+       sta_ptr->tdls_cap.capab = cap;
+
        for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) {
                if (pos + 2 + pos[1] > end)
                        break;
index 08205683f877580f3d937aaaf793acf9a8b26668..96a2126cc44bf81da15f8c17d6dc7895cad5abce 100644 (file)
@@ -55,6 +55,7 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
                return -1;
        }
 
+       memset(rx_info, 0, sizeof(*rx_info));
        rx_info->bss_num = priv->bss_num;
        rx_info->bss_type = priv->bss_type;
 
index ddfc3c6c1e78b6f5d5c3c89768960e1ecf49fe8b..ec7309d096abaf5a11845eea72659374e318fce1 100644 (file)
@@ -174,6 +174,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        }
 
        tx_info = MWIFIEX_SKB_TXCB(skb);
+       memset(tx_info, 0, sizeof(*tx_info));
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
        tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT;
index e11dab2216c6f2c0ed388bae1d41c2b3e50a57a0..573897b8e878a2e6bfc3aa61a9df72d9b99e0372 100644 (file)
@@ -231,9 +231,12 @@ static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer)
  */
 static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev)
 {
-       __le32 reg;
+       __le32 *reg;
        u32 fw_mode;
 
+       reg = kmalloc(sizeof(*reg), GFP_KERNEL);
+       if (reg == NULL)
+               return -ENOMEM;
        /* cannot use rt2x00usb_register_read here as it uses different
         * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the
         * magic value USB_MODE_AUTORUN (0x11) to the device, thus the
@@ -241,8 +244,9 @@ static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE,
                                 USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN,
-                                &reg, sizeof(reg), REGISTER_TIMEOUT_FIRMWARE);
-       fw_mode = le32_to_cpu(reg);
+                                reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE);
+       fw_mode = le32_to_cpu(*reg);
+       kfree(reg);
 
        if ((fw_mode & 0x00000003) == 2)
                return 1;
@@ -261,6 +265,7 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev,
        int status;
        u32 offset;
        u32 length;
+       int retval;
 
        /*
         * Check which section of the firmware we need.
@@ -278,7 +283,10 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev,
        /*
         * Write firmware to device.
         */
-       if (rt2800usb_autorun_detect(rt2x00dev)) {
+       retval = rt2800usb_autorun_detect(rt2x00dev);
+       if (retval < 0)
+               return retval;
+       if (retval) {
                rt2x00_info(rt2x00dev,
                            "Firmware loading not required - NIC in AutoRun mode\n");
        } else {
@@ -763,7 +771,12 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
  */
 static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev)
 {
-       if (rt2800usb_autorun_detect(rt2x00dev))
+       int retval;
+
+       retval = rt2800usb_autorun_detect(rt2x00dev);
+       if (retval < 0)
+               return retval;
+       if (retval)
                return 1;
        return rt2800_efuse_detect(rt2x00dev);
 }
@@ -772,7 +785,10 @@ static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        int retval;
 
-       if (rt2800usb_efuse_detect(rt2x00dev))
+       retval = rt2800usb_efuse_detect(rt2x00dev);
+       if (retval < 0)
+               return retval;
+       if (retval)
                retval = rt2800_read_eeprom_efuse(rt2x00dev);
        else
                retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom,
@@ -1268,6 +1284,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
        { USB_DEVICE(0x043e, 0x7a32) },
+       /* ASUS */
+       { USB_DEVICE(0x0b05, 0x17e8) },
        /* Azurewave */
        { USB_DEVICE(0x13d3, 0x3329) },
        { USB_DEVICE(0x13d3, 0x3365) },
@@ -1304,6 +1322,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x057c, 0x8501) },
        /* Buffalo */
        { USB_DEVICE(0x0411, 0x0241) },
+       { USB_DEVICE(0x0411, 0x0253) },
        /* D-Link */
        { USB_DEVICE(0x2001, 0x3c1a) },
        { USB_DEVICE(0x2001, 0x3c21) },
@@ -1394,6 +1413,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0df6, 0x0053) },
        { USB_DEVICE(0x0df6, 0x0069) },
        { USB_DEVICE(0x0df6, 0x006f) },
+       { USB_DEVICE(0x0df6, 0x0078) },
        /* SMC */
        { USB_DEVICE(0x083a, 0xa512) },
        { USB_DEVICE(0x083a, 0xc522) },
index d50dfac91631ebcbd4f685497ca237d3eb22893f..0bccf123831ec0ab80d8a8c62e99b01d7cd23c12 100644 (file)
@@ -1668,7 +1668,7 @@ static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
 
-       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+       if (test_bit(hlid, &wl->fw_fast_lnk_map))
                thold = wl->conf.tx.fast_link_thold;
        else
                thold = wl->conf.tx.slow_link_thold;
index 7649c75cd68dfd9d189fc724a57522fbad2b9bce..44f0b205b065efa06233387e6545c8021fcf9931 100644 (file)
@@ -78,3 +78,92 @@ out_free:
 out:
        return ret;
 }
+
+int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap)
+{
+       struct wl18xx_cmd_smart_config_start *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config start group_bitmap=0x%x",
+                    group_bitmap);
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->group_id_bitmask = cpu_to_le32(group_bitmap);
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_START, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config start command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
+
+int wl18xx_cmd_smart_config_stop(struct wl1271 *wl)
+{
+       struct wl1271_cmd_header *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_STOP, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
+
+int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key)
+{
+       struct wl18xx_cmd_smart_config_set_group_key *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config set group key id=0x%x",
+                    group_id);
+
+       if (key_len != sizeof(cmd->key)) {
+               wl1271_error("invalid group key size: %d", key_len);
+               return -E2BIG;
+       }
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->group_id = cpu_to_le32(group_id);
+       memcpy(cmd->key, key, key_len);
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_SET_GROUP_KEY, cmd,
+                             sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config set group key cmd");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
index 6687d10899acd4e9a130461ecdddbce07157a783..92499e2dfa83206a83c7992e24fa21157cf776aa 100644 (file)
@@ -45,8 +45,25 @@ struct wl18xx_cmd_channel_switch {
        u8 padding[2];
 } __packed;
 
+struct wl18xx_cmd_smart_config_start {
+       struct wl1271_cmd_header header;
+
+       __le32 group_id_bitmask;
+} __packed;
+
+struct wl18xx_cmd_smart_config_set_group_key {
+       struct wl1271_cmd_header header;
+
+       __le32 group_id;
+
+       u8 key[16];
+} __packed;
+
 int wl18xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
-
+int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap);
+int wl18xx_cmd_smart_config_stop(struct wl1271 *wl);
+int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key);
 #endif
index c9199d7804c634380c25bcb5c126f1ebb22f40fd..eb1848e084242f24e220a3d721b2da51df322b02 100644 (file)
  *
  */
 
+#include <net/genetlink.h>
 #include "event.h"
 #include "scan.h"
 #include "../wlcore/cmd.h"
 #include "../wlcore/debug.h"
+#include "../wlcore/vendor_cmd.h"
 
 int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
                          bool *timeout)
@@ -45,6 +47,58 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
        return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
 }
 
+static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
+                                         u8 sync_band)
+{
+       struct sk_buff *skb;
+       enum ieee80211_band band;
+       int freq;
+
+       if (sync_band == WLCORE_BAND_5GHZ)
+               band = IEEE80211_BAND_5GHZ;
+       else
+               band = IEEE80211_BAND_2GHZ;
+
+       freq = ieee80211_channel_to_frequency(sync_channel, band);
+
+       wl1271_debug(DEBUG_EVENT,
+                    "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)",
+                    freq, sync_channel, sync_band);
+       skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20,
+                                         WLCORE_VENDOR_EVENT_SC_SYNC,
+                                         GFP_KERNEL);
+
+       if (nla_put_u32(skb, WLCORE_VENDOR_ATTR_FREQ, freq)) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+       cfg80211_vendor_event(skb, GFP_KERNEL);
+       return 0;
+}
+
+static int wlcore_smart_config_decode_event(struct wl1271 *wl,
+                                           u8 ssid_len, u8 *ssid,
+                                           u8 pwd_len, u8 *pwd)
+{
+       struct sk_buff *skb;
+
+       wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID");
+       wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len);
+
+       skb = cfg80211_vendor_event_alloc(wl->hw->wiphy,
+                                         ssid_len + pwd_len + 20,
+                                         WLCORE_VENDOR_EVENT_SC_DECODE,
+                                         GFP_KERNEL);
+
+       if (nla_put(skb, WLCORE_VENDOR_ATTR_SSID, ssid_len, ssid) ||
+           nla_put(skb, WLCORE_VENDOR_ATTR_PSK, pwd_len, pwd)) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+       cfg80211_vendor_event(skb, GFP_KERNEL);
+       return 0;
+}
+
 int wl18xx_process_mailbox_events(struct wl1271 *wl)
 {
        struct wl18xx_event_mailbox *mbox = wl->mbox;
@@ -107,5 +161,16 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
        if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
                wlcore_event_roc_complete(wl);
 
+       if (vector & SMART_CONFIG_SYNC_EVENT_ID)
+               wlcore_smart_config_sync_event(wl, mbox->sc_sync_channel,
+                                              mbox->sc_sync_band);
+
+       if (vector & SMART_CONFIG_DECODE_EVENT_ID)
+               wlcore_smart_config_decode_event(wl,
+                                                mbox->sc_ssid_len,
+                                                mbox->sc_ssid,
+                                                mbox->sc_pwd_len,
+                                                mbox->sc_pwd);
+
        return 0;
 }
index a76e98eb8372a9c2f7ff205e87e8a5667a48de38..0680312d49439ca1fe8ea181855c54cb656117be 100644 (file)
@@ -38,6 +38,8 @@ enum {
        REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
        DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
        PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+       SMART_CONFIG_SYNC_EVENT_ID               = BIT(22),
+       SMART_CONFIG_DECODE_EVENT_ID             = BIT(23),
 };
 
 struct wl18xx_event_mailbox {
index de5b4fa5d1666b9a5af57b8312da487a037ebdf4..7af1936719eb879d0915fd9dda2db28ded54f39b 100644 (file)
@@ -992,7 +992,10 @@ static int wl18xx_boot(struct wl1271 *wl)
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID |
-               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
+               SMART_CONFIG_SYNC_EVENT_ID |
+               SMART_CONFIG_DECODE_EVENT_ID;
+;
 
        wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
 
@@ -1606,15 +1609,20 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
                (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
-       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       unsigned long suspend_bitmap;
+
+       /* if we don't have the link map yet, assume they all low prio */
+       if (!status_priv)
+               return false;
 
        /* suspended links are never high priority */
-       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+       suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       if (test_bit(hlid, &suspend_bitmap))
                return false;
 
        /* the priority thresholds are taken from FW */
-       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
-           !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+       if (test_bit(hlid, &wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, &wl->ap_fw_ps_map))
                thold = status_priv->tx_fast_link_prio_threshold;
        else
                thold = status_priv->tx_slow_link_prio_threshold;
@@ -1628,12 +1636,17 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
                (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
-       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       unsigned long suspend_bitmap;
+
+       /* if we don't have the link map yet, assume they all low prio */
+       if (!status_priv)
+               return true;
 
-       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+       suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       if (test_bit(hlid, &suspend_bitmap))
                thold = status_priv->tx_suspend_threshold;
-       else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
-                !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+       else if (test_bit(hlid, &wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, &wl->ap_fw_ps_map))
                thold = status_priv->tx_fast_stop_threshold;
        else
                thold = status_priv->tx_slow_stop_threshold;
@@ -1687,6 +1700,9 @@ static struct wlcore_ops wl18xx_ops = {
        .convert_hwaddr = wl18xx_convert_hwaddr,
        .lnk_high_prio  = wl18xx_lnk_high_prio,
        .lnk_low_prio   = wl18xx_lnk_low_prio,
+       .smart_config_start = wl18xx_cmd_smart_config_start,
+       .smart_config_stop  = wl18xx_cmd_smart_config_stop,
+       .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
index be1ebd55ac88e8f7f04be16e6e7bb02436468cf6..3406ffb53325c16ed47271feb470da95f0f0965e 100644 (file)
@@ -30,7 +30,7 @@
 
 static
 void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
-                            struct ieee80211_tx_rate *rate)
+                            u8 band, struct ieee80211_tx_rate *rate)
 {
        u8 fw_rate = wl->fw_status->counters.tx_last_rate;
 
@@ -43,6 +43,8 @@ void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
 
        if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
                rate->idx = fw_rate;
+               if (band == IEEE80211_BAND_5GHZ)
+                       rate->idx -= CONF_HW_RATE_INDEX_6MBPS;
                rate->flags = 0;
        } else {
                rate->flags = IEEE80211_TX_RC_MCS;
@@ -102,7 +104,8 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
         * first pass info->control.vif while it's valid, and then fill out
         * the info->status structures
         */
-       wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]);
+       wl18xx_get_last_tx_rate(wl, info->control.vif,
+                               info->band, &info->status.rates[0]);
 
        info->status.rates[0].count = 1; /* no data about retries */
        info->status.ack_signal = -1;
index eb7cfe8170104ab5eb273c84f2c49209a0d9e1a3..6a2b88030c1d37bdd0c6aa768e273a6fabff99b5 100644 (file)
@@ -38,7 +38,7 @@
 #define WL18XX_NUM_TX_DESCRIPTORS 32
 #define WL18XX_NUM_RX_DESCRIPTORS 32
 
-#define WL18XX_NUM_MAC_ADDRESSES 3
+#define WL18XX_NUM_MAC_ADDRESSES 2
 
 #define WL18XX_RX_BA_MAX_SESSIONS 13
 
index 4f23931d7bd56237f9e1fc623e784fa059b2340e..0a69c1373643b07fffd482f6a011724d91d9e760 100644 (file)
@@ -1,5 +1,5 @@
 wlcore-objs            = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
-                         boot.o init.o debugfs.o scan.o sysfs.o
+                         boot.o init.o debugfs.o scan.o sysfs.o vendor_cmd.o
 
 wlcore_spi-objs        = spi.o
 wlcore_sdio-objs       = sdio.o
index e269c0a57017a270b0c724abf5426f105fa7343a..05604ee312249cb9f493b270106be2d7ba38913b 100644 (file)
@@ -372,9 +372,9 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        wl1271_tx_reset_link_queues(wl, *hlid);
        wl->links[*hlid].wlvif = NULL;
 
-       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
-           (wlvif->bss_type == BSS_TYPE_AP_BSS &&
-            *hlid == wlvif->ap.bcast_hlid)) {
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+           *hlid == wlvif->ap.bcast_hlid) {
+               u32 sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING;
                /*
                 * save the total freed packets in the wlvif, in case this is
                 * recovery or suspend
@@ -385,9 +385,11 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
                 * increment the initial seq number on recovery to account for
                 * transmitted packets that we haven't yet got in the FW status
                 */
+               if (wlvif->encryption_type == KEY_GEM)
+                       sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
                if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
-                       wlvif->total_freed_pkts +=
-                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+                       wlvif->total_freed_pkts += sqn_padding;
        }
 
        wl->links[*hlid].total_freed_pkts = 0;
index 6788d7356ca5a8d6d4aab0c6d2404985f4a072e1..ca6a28b03f8f31b5b47b23b8db982df4dbf78f84 100644 (file)
@@ -170,6 +170,9 @@ enum wl1271_commands {
 
        /* start of 18xx specific commands */
        CMD_DFS_CHANNEL_CONFIG          = 60,
+       CMD_SMART_CONFIG_START          = 61,
+       CMD_SMART_CONFIG_STOP           = 62,
+       CMD_SMART_CONFIG_SET_GROUP_KEY  = 63,
 
        MAX_COMMAND_ID = 0xFFFF,
 };
index 89893c7170253c8cc5400bf7941cc9ce4a3d3051..0be21f62fcb0eb3a58e7c16886efa8c2bde096cc 100644 (file)
@@ -496,7 +496,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(sg_enabled);
        DRIVER_STATE_PRINT_INT(enable_11a);
        DRIVER_STATE_PRINT_INT(noise);
-       DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
+       DRIVER_STATE_PRINT_LHEX(ap_fw_ps_map);
        DRIVER_STATE_PRINT_LHEX(ap_ps_map);
        DRIVER_STATE_PRINT_HEX(quirks);
        DRIVER_STATE_PRINT_HEX(irq);
index 1555ff9700509186e43996ffb4a7c5bbe106522f..aa9f82c7229673a6c509a7a9fef38287675f083a 100644 (file)
@@ -260,4 +260,31 @@ wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return wl->ops->lnk_low_prio(wl, hlid, lnk);
 }
 
+static inline int
+wlcore_smart_config_start(struct wl1271 *wl, u32 group_bitmap)
+{
+       if (!wl->ops->smart_config_start)
+               return -EINVAL;
+
+       return wl->ops->smart_config_start(wl, group_bitmap);
+}
+
+static inline int
+wlcore_smart_config_stop(struct wl1271 *wl)
+{
+       if (!wl->ops->smart_config_stop)
+               return -EINVAL;
+
+       return wl->ops->smart_config_stop(wl);
+}
+
+static inline int
+wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                 u8 key_len, u8 *key)
+{
+       if (!wl->ops->smart_config_set_group_key)
+               return -EINVAL;
+
+       return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
+}
 #endif
index 48f83868f9cbee35a34871ce078f3615e80827e1..575c8f6d4009477dc6430b7eff91ab6d8a60e080 100644 (file)
@@ -37,6 +37,7 @@
 #include "init.h"
 #include "debugfs.h"
 #include "testmode.h"
+#include "vendor_cmd.h"
 #include "scan.h"
 #include "hw_ops.h"
 #include "sysfs.h"
@@ -332,7 +333,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 {
        bool fw_ps;
 
-       fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       fw_ps = test_bit(hlid, &wl->ap_fw_ps_map);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -359,13 +360,13 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
                                           struct wl_fw_status *status)
 {
-       u32 cur_fw_ps_map;
+       unsigned long cur_fw_ps_map;
        u8 hlid;
 
        cur_fw_ps_map = status->link_ps_bitmap;
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
                wl1271_debug(DEBUG_PSM,
-                            "link ps prev 0x%x cur 0x%x changed 0x%x",
+                            "link ps prev 0x%lx cur 0x%lx changed 0x%lx",
                             wl->ap_fw_ps_map, cur_fw_ps_map,
                             wl->ap_fw_ps_map ^ cur_fw_ps_map);
 
@@ -898,6 +899,44 @@ out:
        wlcore_set_partition(wl, &old_part);
 }
 
+static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  u8 hlid, struct ieee80211_sta *sta)
+{
+       struct wl1271_station *wl_sta;
+       u32 sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING;
+
+       wl_sta = (void *)sta->drv_priv;
+       wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
+
+       /*
+        * increment the initial seq number on recovery to account for
+        * transmitted packets that we haven't yet got in the FW status
+        */
+       if (wlvif->encryption_type == KEY_GEM)
+               sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
+       if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+               wl_sta->total_freed_pkts += sqn_recovery_padding;
+}
+
+static void wlcore_save_freed_pkts_addr(struct wl1271 *wl,
+                                       struct wl12xx_vif *wlvif,
+                                       u8 hlid, const u8 *addr)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+       if (WARN_ON(hlid == WL12XX_INVALID_LINK_ID ||
+                   is_zero_ether_addr(addr)))
+               return;
+
+       rcu_read_lock();
+       sta = ieee80211_find_sta(vif, addr);
+       if (sta)
+               wlcore_save_freed_pkts(wl, wlvif, hlid, sta);
+       rcu_read_unlock();
+}
+
 static void wlcore_print_recovery(struct wl1271 *wl)
 {
        u32 pc = 0;
@@ -961,6 +1000,13 @@ static void wl1271_recovery_work(struct work_struct *work)
                wlvif = list_first_entry(&wl->wlvif_list,
                                       struct wl12xx_vif, list);
                vif = wl12xx_wlvif_to_vif(wlvif);
+
+               if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+                       wlcore_save_freed_pkts_addr(wl, wlvif, wlvif->sta.hlid,
+                                                   vif->bss_conf.bssid);
+               }
+
                __wl1271_op_remove_interface(wl, vif, false);
        }
 
@@ -4703,36 +4749,18 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
 
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
-       struct wl1271_station *wl_sta;
-       struct ieee80211_sta *sta;
-       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
-
        if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
        __clear_bit(hlid, &wl->ap_ps_map);
-       __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       __clear_bit(hlid, &wl->ap_fw_ps_map);
 
        /*
         * save the last used PN in the private part of iee80211_sta,
         * in case of recovery/suspend
         */
-       rcu_read_lock();
-       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
-       if (sta) {
-               wl_sta = (void *)sta->drv_priv;
-               wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
-
-               /*
-                * increment the initial seq number on recovery to account for
-                * transmitted packets that we haven't yet got in the FW status
-                */
-               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
-                       wl_sta->total_freed_pkts +=
-                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
-       }
-       rcu_read_unlock();
+       wlcore_save_freed_pkts_addr(wl, wlvif, hlid, wl->links[hlid].addr);
 
        wl12xx_free_link(wl, wlvif, &hlid);
        wl->active_sta_count--;
@@ -4915,6 +4943,21 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
                clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
        }
 
+       /* save seq number on disassoc (suspend) */
+       if (is_sta &&
+           old_state == IEEE80211_STA_ASSOC &&
+           new_state == IEEE80211_STA_AUTH) {
+               wlcore_save_freed_pkts(wl, wlvif, wlvif->sta.hlid, sta);
+               wlvif->total_freed_pkts = 0;
+       }
+
+       /* restore seq number on assoc (resume) */
+       if (is_sta &&
+           old_state == IEEE80211_STA_AUTH &&
+           new_state == IEEE80211_STA_ASSOC) {
+               wlvif->total_freed_pkts = wl_sta->total_freed_pkts;
+       }
+
        /* clear ROCs on failure or authorization */
        if (is_sta &&
            (new_state == IEEE80211_STA_AUTHORIZED ||
@@ -5149,6 +5192,10 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        if (unlikely(wl->state == WLCORE_STATE_OFF)) {
                wl12xx_for_each_wlvif_sta(wl, wlvif) {
                        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                               continue;
+
                        ieee80211_chswitch_done(vif, false);
                }
                goto out;
@@ -5164,6 +5211,9 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
                unsigned long delay_usec;
 
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
+
                ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
                if (ret)
                        goto out_sleep;
@@ -5619,7 +5669,7 @@ static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
                memcpy(&wl->addresses[idx], &wl->addresses[0],
                       sizeof(wl->addresses[0]));
                /* LAA bit */
-               wl->addresses[idx].addr[2] |= BIT(1);
+               wl->addresses[idx].addr[0] |= BIT(1);
        }
 
        wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
@@ -5764,7 +5814,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
                sizeof(struct ieee80211_header);
 
-       wl->hw->wiphy->max_remain_on_channel_duration = 5000;
+       wl->hw->wiphy->max_remain_on_channel_duration = 30000;
 
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
@@ -5833,6 +5883,9 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->iface_combinations = wl->iface_combinations;
        wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
 
+       /* register vendor commands */
+       wlcore_set_vendor_commands(wl->hw->wiphy);
+
        SET_IEEE80211_DEV(wl->hw, wl->dev);
 
        wl->hw->sta_data_size = sizeof(struct wl1271_station);
index 40b43115f83590b6a6cb4a38d8283e7b78460187..f0ac36139bcc1419a3963a5048e8c3013fce5cd4 100644 (file)
@@ -126,7 +126,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
                return;
 
-       fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       fw_ps = test_bit(hlid, &wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
 
        /*
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
new file mode 100644 (file)
index 0000000..ad86a48
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2014 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "ps.h"
+#include "hw_ops.h"
+#include "vendor_cmd.h"
+
+static const
+struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
+       [WLCORE_VENDOR_ATTR_FREQ]               = { .type = NLA_U32 },
+       [WLCORE_VENDOR_ATTR_GROUP_ID]           = { .type = NLA_U32 },
+       [WLCORE_VENDOR_ATTR_GROUP_KEY]          = { .type = NLA_U32,
+                                                   .len = WLAN_MAX_KEY_LEN },
+};
+
+static int
+wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
+                                    struct wireless_dev *wdev,
+                                    const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
+
+       if (!data)
+               return -EINVAL;
+
+       ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
+                       wlcore_vendor_attr_policy);
+       if (ret)
+               return ret;
+
+       if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
+               return -EINVAL;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_start(wl,
+                       nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return 0;
+}
+
+static int
+wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
+                                   struct wireless_dev *wdev,
+                                   const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_stop(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static int
+wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
+                                            struct wireless_dev *wdev,
+                                            const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
+
+       if (!data)
+               return -EINVAL;
+
+       ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
+                       wlcore_vendor_attr_policy);
+       if (ret)
+               return ret;
+
+       if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
+           !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
+               return -EINVAL;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_set_group_key(wl,
+                       nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
+                       nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
+                       nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_start,
+       },
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_stop,
+       },
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_set_group_key,
+       },
+};
+
+static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
+       {
+               .vendor_id = TI_OUI,
+               .subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
+       },
+       {
+               .vendor_id = TI_OUI,
+               .subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
+       },
+};
+
+void wlcore_set_vendor_commands(struct wiphy *wiphy)
+{
+       wiphy->vendor_commands = wlcore_vendor_commands;
+       wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
+       wiphy->vendor_events = wlcore_vendor_events;
+       wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
+}
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.h b/drivers/net/wireless/ti/wlcore/vendor_cmd.h
new file mode 100644 (file)
index 0000000..6e0c15e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2014 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef __WLCORE_VENDOR_H__
+#define __WLCORE_VENDOR_H__
+
+#ifdef __KERNEL__
+void wlcore_set_vendor_commands(struct wiphy *wiphy);
+#endif
+
+#define TI_OUI 0x080028
+
+enum wlcore_vendor_commands {
+       WLCORE_VENDOR_CMD_SMART_CONFIG_START,
+       WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
+       WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
+
+       NUM_WLCORE_VENDOR_CMD,
+       MAX_WLCORE_VENDOR_CMD = NUM_WLCORE_VENDOR_CMD - 1
+};
+
+enum wlcore_vendor_attributes {
+       WLCORE_VENDOR_ATTR_FREQ,
+       WLCORE_VENDOR_ATTR_PSK,
+       WLCORE_VENDOR_ATTR_SSID,
+       WLCORE_VENDOR_ATTR_GROUP_ID,
+       WLCORE_VENDOR_ATTR_GROUP_KEY,
+
+       NUM_WLCORE_VENDOR_ATTR,
+       MAX_WLCORE_VENDOR_ATTR = NUM_WLCORE_VENDOR_ATTR - 1
+};
+
+enum wlcore_vendor_events {
+       WLCORE_VENDOR_EVENT_SC_SYNC,
+       WLCORE_VENDOR_EVENT_SC_DECODE,
+};
+
+#endif /* __WLCORE_VENDOR_H__ */
index 71320509b56d5bc3133d602888092a6e566cb89e..df78cf12ef1574b475751aca21ab5995c397af95 100644 (file)
@@ -117,6 +117,10 @@ struct wlcore_ops {
                              struct wl1271_link *lnk);
        bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
                             struct wl1271_link *lnk);
+       int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap);
+       int (*smart_config_stop)(struct wl1271 *wl);
+       int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key);
 };
 
 enum wlcore_partitions {
@@ -384,10 +388,10 @@ struct wl1271 {
        int active_link_count;
 
        /* Fast/slow links bitmap according to FW */
-       u32 fw_fast_lnk_map;
+       unsigned long fw_fast_lnk_map;
 
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
-       u32 ap_fw_ps_map;
+       unsigned long ap_fw_ps_map;
 
        /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
        unsigned long ap_ps_map;
index c2c34a84ff3d4bf7231f84933564b0e78dcc415b..0e52556044d9c0621c4c15b44e3fc6a7c709f579 100644 (file)
@@ -45,6 +45,9 @@
 #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
 #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
 #define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff
+/* Use smaller padding for GEM, as some  APs have issues when it's too big */
+#define WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20
+
 
 #define WL1271_CIPHER_SUITE_GEM 0x00147201
 
@@ -324,6 +327,7 @@ struct wl1271_station {
         * total freed FW packets on the link to the STA - used for tracking the
         * AES/TKIP PN across recoveries. Re-initialized each time from the
         * wl1271_station structure.
+        * Used in both AP and STA mode.
         */
        u64 total_freed_pkts;
 };
@@ -459,6 +463,13 @@ struct wl12xx_vif {
        /* work for canceling ROC after pending auth reply */
        struct delayed_work pending_auth_complete_work;
 
+       /*
+        * total freed FW packets on the link.
+        * For STA this holds the PN of the link to the AP.
+        * For AP this holds the PN of the broadcast link.
+        */
+       u64 total_freed_pkts;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -466,15 +477,6 @@ struct wl12xx_vif {
         */
        struct {
                u8 persistent[0];
-
-               /*
-                * total freed FW packets on the link - used for
-                * storing the AES/TKIP PN during recovery, as this
-                * structure is not zeroed out.
-                * For STA this holds the PN of the link to the AP.
-                * For AP this holds the PN of the broadcast link.
-                */
-               u64 total_freed_pkts;
        };
 };
 
index 96c8e1de0879cf4f78952d1487c9dac2137e4572..95920581860afc19f38383d3880bd81f5e45f1e7 100644 (file)
@@ -3,11 +3,11 @@ config ZD1211RW
        depends on USB && MAC80211
        select FW_LOADER
        ---help---
-         This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
+         This is a driver for the ZyDAS ZD1211/ZD1211B wireless
          chip, present in many USB-wireless adapters.
 
          Device firmware is required alongside this driver. You can download
-         the firmware distribution from http://zd1211.ath.cx/get-firmware
+         the firmware distribution from http://sf.net/projects/zd1211/files/
 
 config ZD1211RW_DEBUG
        bool "ZyDAS ZD1211 debugging"
index a8dc95ebf2d605a8a774c6b05b9287dfaa3b7846..0f28c08fcb3c76f4a2deaf3cd084a39f800db142 100644 (file)
@@ -326,13 +326,13 @@ err_ctlreg:
        return err;
 }
 
-static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
-                              u16 mask, u16 shift)
+static s8 sprom_extract_antgain(u8 sprom_revision, const u16 *in, u16 offset,
+                               u16 mask, u16 shift)
 {
        u16 v;
        u8 gain;
 
-       v = in[SPOFF(SSB_SPROM1_AGAIN)];
+       v = in[SPOFF(offset)];
        gain = (v & mask) >> shift;
        if (gain == 0xFF)
                gain = 2; /* If unset use 2dBm */
@@ -416,12 +416,14 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
        SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
 
        /* Extract the antenna gain values. */
-       out->antenna_gain.a0 = r123_extract_antgain(out->revision, in,
-                                                   SSB_SPROM1_AGAIN_BG,
-                                                   SSB_SPROM1_AGAIN_BG_SHIFT);
-       out->antenna_gain.a1 = r123_extract_antgain(out->revision, in,
-                                                   SSB_SPROM1_AGAIN_A,
-                                                   SSB_SPROM1_AGAIN_A_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM1_AGAIN,
+                                                    SSB_SPROM1_AGAIN_BG,
+                                                    SSB_SPROM1_AGAIN_BG_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM1_AGAIN,
+                                                    SSB_SPROM1_AGAIN_A,
+                                                    SSB_SPROM1_AGAIN_A_SHIFT);
        if (out->revision >= 2)
                sprom_extract_r23(out, in);
 }
@@ -468,7 +470,15 @@ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
 
 static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 {
+       static const u16 pwr_info_offset[] = {
+               SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1,
+               SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3
+       };
        u16 il0mac_offset;
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
+                    ARRAY_SIZE(out->core_pwr_info));
 
        if (out->revision == 4)
                il0mac_offset = SSB_SPROM4_IL0MAC;
@@ -524,14 +534,59 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
        }
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01,
-            SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01,
-            SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23,
-            SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23,
-            SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN01,
+                                                    SSB_SPROM4_AGAIN0,
+                                                    SSB_SPROM4_AGAIN0_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN01,
+                                                    SSB_SPROM4_AGAIN1,
+                                                    SSB_SPROM4_AGAIN1_SHIFT);
+       out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN23,
+                                                    SSB_SPROM4_AGAIN2,
+                                                    SSB_SPROM4_AGAIN2_SHIFT);
+       out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN23,
+                                                    SSB_SPROM4_AGAIN3,
+                                                    SSB_SPROM4_AGAIN3_SHIFT);
+
+       /* Extract cores power info info */
+       for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
+               u16 o = pwr_info_offset[i];
+
+               SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
+                       SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT);
+               SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
+                       SSB_SPROM4_2G_MAXP, 0);
+
+               SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0);
+
+               SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
+                       SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT);
+               SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
+                       SSB_SPROM4_5G_MAXP, 0);
+               SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP,
+                       SSB_SPROM4_5GH_MAXP, 0);
+               SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP,
+                       SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT);
+
+               SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0);
+       }
 
        sprom_extract_r458(out, in);
 
@@ -621,14 +676,22 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
        SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN01,
+                                                    SSB_SPROM8_AGAIN0,
+                                                    SSB_SPROM8_AGAIN0_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN01,
+                                                    SSB_SPROM8_AGAIN1,
+                                                    SSB_SPROM8_AGAIN1_SHIFT);
+       out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN23,
+                                                    SSB_SPROM8_AGAIN2,
+                                                    SSB_SPROM8_AGAIN2_SHIFT);
+       out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN23,
+                                                    SSB_SPROM8_AGAIN3,
+                                                    SSB_SPROM8_AGAIN3_SHIFT);
 
        /* Extract cores power info info */
        for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
index 0b3bb16c705a2eadc841d56bf177a900e7a8166e..969af0f2bdf92cdaf14d5e040a6b76f70dd32984 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/bcma/bcma_driver_chipcommon.h>
 #include <linux/bcma/bcma_driver_pci.h>
+#include <linux/bcma/bcma_driver_pcie2.h>
 #include <linux/bcma/bcma_driver_mips.h>
 #include <linux/bcma/bcma_driver_gmac_cmn.h>
 #include <linux/ssb/ssb.h> /* SPROM sharing */
@@ -157,6 +158,8 @@ struct bcma_host_ops {
 /* Chip IDs of PCIe devices */
 #define BCMA_CHIP_ID_BCM4313   0x4313
 #define BCMA_CHIP_ID_BCM43142  43142
+#define BCMA_CHIP_ID_BCM43217  43217
+#define BCMA_CHIP_ID_BCM43222  43222
 #define BCMA_CHIP_ID_BCM43224  43224
 #define  BCMA_PKG_ID_BCM43224_FAB_CSM  0x8
 #define  BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa
@@ -333,6 +336,7 @@ struct bcma_bus {
 
        struct bcma_drv_cc drv_cc;
        struct bcma_drv_pci drv_pci[2];
+       struct bcma_drv_pcie2 drv_pcie2;
        struct bcma_drv_mips drv_mips;
        struct bcma_drv_gmac_cmn drv_gmac_cmn;
 
diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h
new file mode 100644 (file)
index 0000000..5988b05
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef LINUX_BCMA_DRIVER_PCIE2_H_
+#define LINUX_BCMA_DRIVER_PCIE2_H_
+
+#define BCMA_CORE_PCIE2_CLK_CONTROL            0x0000
+#define  PCIE2_CLKC_RST_OE                     0x0001 /* When set, drives PCI_RESET out to pin */
+#define  PCIE2_CLKC_RST                                0x0002 /* Value driven out to pin */
+#define  PCIE2_CLKC_SPERST                     0x0004 /* SurvivePeRst */
+#define  PCIE2_CLKC_DISABLE_L1CLK_GATING       0x0010
+#define  PCIE2_CLKC_DLYPERST                   0x0100 /* Delay PeRst to CoE Core */
+#define  PCIE2_CLKC_DISSPROMLD                 0x0200 /* DisableSpromLoadOnPerst */
+#define  PCIE2_CLKC_WAKE_MODE_L2               0x1000 /* Wake on L2 */
+#define BCMA_CORE_PCIE2_RC_PM_CONTROL          0x0004
+#define BCMA_CORE_PCIE2_RC_PM_STATUS           0x0008
+#define BCMA_CORE_PCIE2_EP_PM_CONTROL          0x000C
+#define BCMA_CORE_PCIE2_EP_PM_STATUS           0x0010
+#define BCMA_CORE_PCIE2_EP_LTR_CONTROL         0x0014
+#define BCMA_CORE_PCIE2_EP_LTR_STATUS          0x0018
+#define BCMA_CORE_PCIE2_EP_OBFF_STATUS         0x001C
+#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS                0x0020
+#define BCMA_CORE_PCIE2_RC_AXI_CONFIG          0x0100
+#define BCMA_CORE_PCIE2_EP_AXI_CONFIG          0x0104
+#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0                0x0108
+#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0       0x010C
+#define BCMA_CORE_PCIE2_CONFIGINDADDR          0x0120
+#define BCMA_CORE_PCIE2_CONFIGINDDATA          0x0124
+#define BCMA_CORE_PCIE2_MDIOCONTROL            0x0128
+#define BCMA_CORE_PCIE2_MDIOWRDATA             0x012C
+#define BCMA_CORE_PCIE2_MDIORDDATA             0x0130
+#define BCMA_CORE_PCIE2_DATAINTF               0x0180
+#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0         0x0188
+#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0         0x018c
+#define BCMA_CORE_PCIE2_H2D_INTSTAT_0          0x0190
+#define BCMA_CORE_PCIE2_H2D_INTMASK_0          0x0194
+#define BCMA_CORE_PCIE2_D2H_INTSTAT_0          0x0198
+#define BCMA_CORE_PCIE2_D2H_INTMASK_0          0x019c
+#define BCMA_CORE_PCIE2_LTR_STATE              0x01A0 /* Latency Tolerance Reporting */
+#define  PCIE2_LTR_ACTIVE                      2
+#define  PCIE2_LTR_ACTIVE_IDLE                 1
+#define  PCIE2_LTR_SLEEP                       0
+#define  PCIE2_LTR_FINAL_MASK                  0x300
+#define  PCIE2_LTR_FINAL_SHIFT                 8
+#define BCMA_CORE_PCIE2_PWR_INT_STATUS         0x01A4
+#define BCMA_CORE_PCIE2_PWR_INT_MASK           0x01A8
+#define BCMA_CORE_PCIE2_CFG_ADDR               0x01F8
+#define BCMA_CORE_PCIE2_CFG_DATA               0x01FC
+#define BCMA_CORE_PCIE2_SYS_EQ_PAGE            0x0200
+#define BCMA_CORE_PCIE2_SYS_MSI_PAGE           0x0204
+#define BCMA_CORE_PCIE2_SYS_MSI_INTREN         0x0208
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0          0x0210
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1          0x0214
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2          0x0218
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3          0x021C
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4          0x0220
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5          0x0224
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0           0x0250
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0           0x0254
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1           0x0258
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1           0x025C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2           0x0260
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2           0x0264
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3           0x0268
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3           0x026C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4           0x0270
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4           0x0274
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5           0x0278
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5           0x027C
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN         0x0330
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR                0x0334
+#define BCMA_CORE_PCIE2_SYS_MSI_REQ            0x0340
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN       0x0344
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR      0x0348
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR0         0x0350
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR1         0x0354
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR2         0x0358
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR3         0x035C
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0         0x0360
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1         0x0364
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0                0x0370
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1                0x0374
+#define BCMA_CORE_PCIE2_SPROM(wordoffset)      (0x0800 + ((wordoffset) * 2))
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0          0x0C00
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1          0x0C04
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2          0x0C08
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3          0x0C0C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4          0x0C10
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5          0x0C14
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6          0x0C18
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7          0x0C1C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0          0x0C20
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1          0x0C24
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2          0x0C28
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3          0x0C2C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4          0x0C30
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5          0x0C34
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6          0x0C38
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7          0x0C3C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP1            0x0C80
+#define BCMA_CORE_PCIE2_FUNC1_IMAP1            0x0C88
+#define BCMA_CORE_PCIE2_FUNC0_IMAP2            0x0CC0
+#define BCMA_CORE_PCIE2_FUNC1_IMAP2            0x0CC8
+#define BCMA_CORE_PCIE2_IARR0_LOWER            0x0D00
+#define BCMA_CORE_PCIE2_IARR0_UPPER            0x0D04
+#define BCMA_CORE_PCIE2_IARR1_LOWER            0x0D08
+#define BCMA_CORE_PCIE2_IARR1_UPPER            0x0D0C
+#define BCMA_CORE_PCIE2_IARR2_LOWER            0x0D10
+#define BCMA_CORE_PCIE2_IARR2_UPPER            0x0D14
+#define BCMA_CORE_PCIE2_OARR0                  0x0D20
+#define BCMA_CORE_PCIE2_OARR1                  0x0D28
+#define BCMA_CORE_PCIE2_OARR2                  0x0D30
+#define BCMA_CORE_PCIE2_OMAP0_LOWER            0x0D40
+#define BCMA_CORE_PCIE2_OMAP0_UPPER            0x0D44
+#define BCMA_CORE_PCIE2_OMAP1_LOWER            0x0D48
+#define BCMA_CORE_PCIE2_OMAP1_UPPER            0x0D4C
+#define BCMA_CORE_PCIE2_OMAP2_LOWER            0x0D50
+#define BCMA_CORE_PCIE2_OMAP2_UPPER            0x0D54
+#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE       0x0D58
+#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE       0x0D5C
+#define BCMA_CORE_PCIE2_MEM_CONTROL            0x0F00
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0                0x0F04
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1                0x0F08
+#define BCMA_CORE_PCIE2_LINK_STATUS            0x0F0C
+#define BCMA_CORE_PCIE2_STRAP_STATUS           0x0F10
+#define BCMA_CORE_PCIE2_RESET_STATUS           0x0F14
+#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN    0x0F18
+#define BCMA_CORE_PCIE2_MISC_INTR_EN           0x0F1C
+#define BCMA_CORE_PCIE2_TX_DEBUG_CFG           0x0F20
+#define BCMA_CORE_PCIE2_MISC_CONFIG            0x0F24
+#define BCMA_CORE_PCIE2_MISC_STATUS            0x0F28
+#define BCMA_CORE_PCIE2_INTR_EN                        0x0F30
+#define BCMA_CORE_PCIE2_INTR_CLEAR             0x0F34
+#define BCMA_CORE_PCIE2_INTR_STATUS            0x0F38
+
+/* PCIE gen2 config regs */
+#define PCIE2_INTSTATUS                                0x090
+#define PCIE2_INTMASK                          0x094
+#define PCIE2_SBMBX                            0x098
+
+#define PCIE2_PMCR_REFUP                       0x1814 /* Trefup time */
+
+#define PCIE2_CAP_DEVSTSCTRL2_OFFSET           0xD4
+#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB          0x400
+#define PCIE2_PVT_REG_PM_CLK_PERIOD            0x184c
+
+struct bcma_drv_pcie2 {
+       struct bcma_device *core;
+};
+
+#define pcie2_read16(pcie2, offset)            bcma_read16((pcie2)->core, offset)
+#define pcie2_read32(pcie2, offset)            bcma_read32((pcie2)->core, offset)
+#define pcie2_write16(pcie2, offset, val)      bcma_write16((pcie2)->core, offset, val)
+#define pcie2_write32(pcie2, offset, val)      bcma_write32((pcie2)->core, offset, val)
+
+#define pcie2_set32(pcie2, offset, set)                bcma_set32((pcie2)->core, offset, set)
+#define pcie2_mask32(pcie2, offset, mask)      bcma_mask32((pcie2)->core, offset, mask)
+
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
+
+#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */
index 75d17e15da338c1bcb05d1b27283ea577050b3aa..63ab3873c5ed00ac6fa36e73892d4db709c13bf0 100644 (file)
@@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
        u8 oui_type;
 } __packed;
 
+struct ieee80211_wmm_ac_param {
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+       __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+       u8 element_id; /* Element ID: 221 (0xdd); */
+       u8 len; /* Length: 24 */
+       /* required fields for WMM version 1 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 1 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specific QoS info */
+       u8 reserved; /* 0 */
+       /* AC_BE, AC_BK, AC_VI, AC_VO */
+       struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+
 /* Control frames */
 struct ieee80211_rts {
        __le16 frame_control;
index f9f931c89e3e2deeda4da48138b6237db968b9c9..f7b9100686c3dd4951bebcd6543b869905a208df 100644 (file)
 #define  SSB_SPROM4_TXPID5GH2_SHIFT    0
 #define  SSB_SPROM4_TXPID5GH3          0xFF00
 #define  SSB_SPROM4_TXPID5GH3_SHIFT    8
+
+/* There are 4 blocks with power info sharing the same layout */
+#define SSB_SPROM4_PWR_INFO_CORE0      0x0080
+#define SSB_SPROM4_PWR_INFO_CORE1      0x00AE
+#define SSB_SPROM4_PWR_INFO_CORE2      0x00DC
+#define SSB_SPROM4_PWR_INFO_CORE3      0x010A
+
+#define SSB_SPROM4_2G_MAXP_ITSSI       0x00    /* 2 GHz ITSSI and 2 GHz Max Power */
+#define  SSB_SPROM4_2G_MAXP            0x00FF
+#define  SSB_SPROM4_2G_ITSSI           0xFF00
+#define  SSB_SPROM4_2G_ITSSI_SHIFT     8
+#define SSB_SPROM4_2G_PA_0             0x02    /* 2 GHz power amp */
+#define SSB_SPROM4_2G_PA_1             0x04
+#define SSB_SPROM4_2G_PA_2             0x06
+#define SSB_SPROM4_2G_PA_3             0x08
+#define SSB_SPROM4_5G_MAXP_ITSSI       0x0A    /* 5 GHz ITSSI and 5.3 GHz Max Power */
+#define  SSB_SPROM4_5G_MAXP            0x00FF
+#define  SSB_SPROM4_5G_ITSSI           0xFF00
+#define  SSB_SPROM4_5G_ITSSI_SHIFT     8
+#define SSB_SPROM4_5GHL_MAXP           0x0C    /* 5.2 GHz and 5.8 GHz Max Power */
+#define  SSB_SPROM4_5GH_MAXP           0x00FF
+#define  SSB_SPROM4_5GL_MAXP           0xFF00
+#define  SSB_SPROM4_5GL_MAXP_SHIFT     8
+#define SSB_SPROM4_5G_PA_0             0x0E    /* 5.3 GHz power amp */
+#define SSB_SPROM4_5G_PA_1             0x10
+#define SSB_SPROM4_5G_PA_2             0x12
+#define SSB_SPROM4_5G_PA_3             0x14
+#define SSB_SPROM4_5GL_PA_0            0x16    /* 5.2 GHz power amp */
+#define SSB_SPROM4_5GL_PA_1            0x18
+#define SSB_SPROM4_5GL_PA_2            0x1A
+#define SSB_SPROM4_5GL_PA_3            0x1C
+#define SSB_SPROM4_5GH_PA_0            0x1E    /* 5.8 GHz power amp */
+#define SSB_SPROM4_5GH_PA_1            0x20
+#define SSB_SPROM4_5GH_PA_2            0x22
+#define SSB_SPROM4_5GH_PA_3            0x24
+
+/* TODO: Make it deprecated */
 #define SSB_SPROM4_MAXP_BG             0x0080  /* Max Power BG in path 1 */
 #define  SSB_SPROM4_MAXP_BG_MASK       0x00FF  /* Mask for Max Power BG */
 #define  SSB_SPROM4_ITSSI_BG           0xFF00  /* Mask for path 1 itssi_bg */
index 9ce5cb17ed826a83979d787b9a43e0f19d14f383..dae2e24616e16bf3051af3dd31414f91f4220af9 100644 (file)
@@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
  */
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
 
+/**
+ * ieee80211_start_rx_ba_session_offl - start a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Create structures responsible for reordering so device drivers may call here
+ * when they complete AddBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid);
+
+/**
+ * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Destroy structures responsible for reordering so device drivers may call here
+ * when they complete DelBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid);
+
 /* Rate control API */
 
 /**
index 31bf2586fb84a59a0b8ce964c1717e2555a7ea65..f0e84bc48038932b022a0623ab1e2bf11a9fdfaf 100644 (file)
@@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
        del_timer_sync(&tid_rx->reorder_timer);
 
        for (i = 0; i < tid_rx->buf_size; i++)
-               dev_kfree_skb(tid_rx->reorder_buf[i]);
+               __skb_queue_purge(&tid_rx->reorder_buf[i]);
        kfree(tid_rx->reorder_buf);
        kfree(tid_rx->reorder_time);
        kfree(tid_rx);
@@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        ieee80211_tx_skb(sdata, skb);
 }
 
-void ieee80211_process_addba_request(struct ieee80211_local *local,
-                                    struct sta_info *sta,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len)
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx)
 {
+       struct ieee80211_local *local = sta->sdata->local;
        struct tid_ampdu_rx *tid_agg_rx;
-       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
-       u8 dialog_token;
-       int ret = -EOPNOTSUPP;
-
-       /* extract session parameters from addba request frame */
-       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
-       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
-       start_seq_num =
-               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
-
-       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
-       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
-       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
-       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
-
-       status = WLAN_STATUS_REQUEST_DECLINED;
+       int i, ret = -EOPNOTSUPP;
+       u16 status = WLAN_STATUS_REQUEST_DECLINED;
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
                ht_dbg(sta->sdata,
@@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                status = WLAN_STATUS_INVALID_QOS_PARAM;
                ht_dbg_ratelimited(sta->sdata,
                                   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
-                                  mgmt->sa, tid, ba_policy, buf_size);
+                                  sta->sta.addr, tid, ba_policy, buf_size);
                goto end_no_lock;
        }
        /* determine default buffer size */
@@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        if (sta->ampdu_mlme.tid_rx[tid]) {
                ht_dbg_ratelimited(sta->sdata,
                                   "unexpected AddBA Req from %pM on tid %u\n",
-                                  mgmt->sa, tid);
+                                  sta->sta.addr, tid);
 
                /* delete existing Rx BA session on the same tid */
                ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
@@ -308,7 +295,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        /* prepare reordering buffer */
        tid_agg_rx->reorder_buf =
-               kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
+               kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
        tid_agg_rx->reorder_time =
                kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
        if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
@@ -318,6 +305,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                goto end;
        }
 
+       for (i = 0; i < buf_size; i++)
+               __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
+
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
                               &sta->sta, tid, &start_seq_num, 0);
        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
@@ -350,6 +340,74 @@ end:
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
 end_no_lock:
-       ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
-                                 dialog_token, status, 1, buf_size, timeout);
+       if (tx)
+               ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
+                                         dialog_token, status, 1, buf_size,
+                                         timeout);
+}
+
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+                                    struct sta_info *sta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
+       u8 dialog_token;
+
+       /* extract session parameters from addba request frame */
+       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+       start_seq_num =
+               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+
+       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+
+       __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
+                                       start_seq_num, ba_policy, tid,
+                                       buf_size, true);
+}
+
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
+}
+EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
+
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 }
+EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
index c3fd4d275bf42dbd77ebd12f6b7212fcc5f9817c..6d537f03c0baa0450eefc5c22f496be085072cfd 100644 (file)
@@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
 static struct ieee80211_chanctx *
 ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local __maybe_unused = sdata->local;
        struct ieee80211_chanctx_conf *conf;
 
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
index 15702ff64a4c89fb89541f620b9da7dcccc2a7bd..ff630be2ca750182fabfce3ea363a44cd24d7928 100644 (file)
@@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 
        /*
         * If user has specified capability over-rides, take care
-        * of that if the station we're setting up is the AP that
+        * of that if the station we're setting up is the AP or TDLS peer that
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
-            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
-           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+           sdata->vif.type == NL80211_IFTYPE_ADHOC)
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
        /*
@@ -228,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
        if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
                ht_cap.mcs.rx_mask[32/8] |= 1;
 
+       /* set Rx highest rate */
+       ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
+
  apply:
        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 
index 713485f9effc01000d33369b056236307822f838..9713dc54ea4bb385abf1d48ae891ff5fceb7440f 100644 (file)
@@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                                                 chandef, 0);
        }
 
-       if (local->hw.queues >= IEEE80211_NUM_ACS) {
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = 0; /* U-APSD no in use */
-       }
+       if (local->hw.queues >= IEEE80211_NUM_ACS)
+               pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
 
        presp->head_len = pos - presp->head;
        if (WARN_ON(presp->head_len > frame_len))
index 9e025e1184cc3b95fb9adfcdc768f0cecf5e35ee..ef7a089ac54647eb763b8bbb0793bef98454b48d 100644 (file)
@@ -345,7 +345,6 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_CONNECTION_POLL   = BIT(1),
        IEEE80211_STA_CONTROL_PORT      = BIT(2),
        IEEE80211_STA_DISABLE_HT        = BIT(4),
-       IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
        IEEE80211_STA_UAPSD_ENABLED     = BIT(7),
        IEEE80211_STA_NULLFUNC_ACKED    = BIT(8),
@@ -503,6 +502,9 @@ struct ieee80211_if_managed {
        struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
        struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
        struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+
+       u8 tdls_peer[ETH_ALEN] __aligned(2);
+       struct delayed_work tdls_peer_del_work;
 };
 
 struct ieee80211_if_ibss {
@@ -815,9 +817,6 @@ struct ieee80211_sub_if_data {
        bool radar_required;
        struct delayed_work dfs_cac_timer_work;
 
-       u8 tdls_peer[ETH_ALEN] __aligned(2);
-       struct delayed_work tdls_peer_del_work;
-
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@ -926,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
        return shift;
 }
 
+struct ieee80211_rx_agg {
+       u8 addr[ETH_ALEN];
+       u16 tid;
+};
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
        IEEE80211_SDATA_QUEUE_AGG_STOP          = 2,
+       IEEE80211_SDATA_QUEUE_RX_AGG_START      = 3,
+       IEEE80211_SDATA_QUEUE_RX_AGG_STOP       = 4,
 };
 
 enum {
@@ -1578,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
 void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool stop);
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx);
 void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
                                         enum ieee80211_agg_stop_reason reason);
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@ -1730,6 +1740,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
 }
 
+static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
+{
+       struct sk_buff *tail = skb_peek_tail(frames);
+       struct ieee80211_rx_status *status;
+
+       if (!tail)
+               return false;
+
+       status = IEEE80211_SKB_RXCB(tail);
+       if (status->flag & RX_FLAG_AMSDU_MORE)
+               return false;
+
+       return true;
+}
+
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_timer(unsigned long data);
@@ -1824,6 +1849,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, bool need_basic,
                                enum ieee80211_band band);
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 
 /* channel management */
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
index bbf51b2f0651cf17e3dfec900fc2ce725b28c75d..29be8854a027556310482298c152894b34cf8d2b 100644 (file)
@@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
        struct sk_buff *skb;
        struct sta_info *sta;
        struct ieee80211_ra_tid *ra_tid;
+       struct ieee80211_rx_agg *rx_agg;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
                        ra_tid = (void *)&skb->cb;
                        ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
                                                ra_tid->tid);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta) {
+                               u16 last_seq;
+
+                               last_seq = le16_to_cpu(
+                                       sta->last_seq_ctrl[rx_agg->tid]);
+
+                               __ieee80211_start_rx_ba_session(sta,
+                                               0, 0,
+                                               ieee80211_sn_inc(last_seq),
+                                               1, rx_agg->tid,
+                                               IEEE80211_MAX_AMPDU_BUF,
+                                               false);
+                       }
+                       mutex_unlock(&local->sta_mtx);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta)
+                               __ieee80211_stop_rx_ba_session(sta,
+                                                       rx_agg->tid,
+                                                       WLAN_BACK_RECIPIENT, 0,
+                                                       false);
+                       mutex_unlock(&local->sta_mtx);
                } else if (ieee80211_is_action(mgmt->frame_control) &&
                           mgmt->u.action.category == WLAN_CATEGORY_BACK) {
                        int len = skb->len;
@@ -1672,8 +1701,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                          ieee80211_dfs_cac_timer_work);
        INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
                          ieee80211_delayed_tailroom_dec);
-       INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
-                         ieee80211_tdls_peer_del_work);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
index 16d97f044a202e61020f3ef563941e4c9adace0a..d808cff8015374ddfb927d5695a3d3973bcc0428 100644 (file)
@@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
        int idx, ret;
        bool pairwise;
 
-       if (WARN_ON(!sdata || !key))
-               return -EINVAL;
-
        pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
        idx = key->conf.keyidx;
        key->local = sdata->local;
index 931330bbe00c589c33393c6b3869737641c9b307..31a8afaf73323bc09fb99f8e960b17c3fa2e31fd 100644 (file)
@@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        qos_info = 0;
                }
 
-               pos = skb_put(skb, 9);
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = qos_info;
+               pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
        }
 
        /* add any remaining custom (i.e. vendor specific here) IEs */
@@ -1005,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                sdata->csa_block_tx = false;
        }
 
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
 
@@ -1064,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        /* disregard subsequent announcements if we are already processing */
-       if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
+       if (sdata->vif.csa_active)
                return;
 
        current_band = cbss->channel->band;
@@ -1091,8 +1080,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
-       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-
        mutex_lock(&local->mtx);
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
@@ -2108,8 +2095,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               true, frame_buf);
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
        if (sdata->csa_block_tx) {
@@ -3722,6 +3707,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
        INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
+       INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
+                         ieee80211_tdls_peer_del_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -4585,6 +4572,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        cancel_work_sync(&ifmgd->request_smps_work);
        cancel_work_sync(&ifmgd->csa_connection_drop_work);
        cancel_work_sync(&ifmgd->chswitch_work);
+       cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
 
        sdata_lock(sdata);
        if (ifmgd->assoc_data) {
index 5f572bed176100d634a64ba5fcc8af181ce543be..bd2c9b22c945669f8ec1d48e3959101fd9ce6754 100644 (file)
@@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
                                            int index,
                                            struct sk_buff_head *frames)
 {
-       struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+       struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
+       struct sk_buff *skb;
        struct ieee80211_rx_status *status;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
-       if (!skb)
+       if (skb_queue_empty(skb_list))
+               goto no_frame;
+
+       if (!ieee80211_rx_reorder_ready(skb_list)) {
+               __skb_queue_purge(skb_list);
                goto no_frame;
+       }
 
-       /* release the frame from the reorder ring buffer */
+       /* release frames from the reorder ring buffer */
        tid_agg_rx->stored_mpdu_num--;
-       tid_agg_rx->reorder_buf[index] = NULL;
-       status = IEEE80211_SKB_RXCB(skb);
-       status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
-       __skb_queue_tail(frames, skb);
+       while ((skb = __skb_dequeue(skb_list))) {
+               status = IEEE80211_SKB_RXCB(skb);
+               status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
+               __skb_queue_tail(frames, skb);
+       }
 
 no_frame:
        tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
@@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                          struct tid_ampdu_rx *tid_agg_rx,
                                          struct sk_buff_head *frames)
 {
-       int index, j;
+       int index, i, j;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        /* release the buffer until next missing frame */
        index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
-       if (!tid_agg_rx->reorder_buf[index] &&
+       if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
            tid_agg_rx->stored_mpdu_num) {
                /*
                 * No buffers ready to be released, but check whether any
@@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                int skipped = 1;
                for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (!tid_agg_rx->reorder_buf[j]) {
+                       if (!ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j])) {
                                skipped++;
                                continue;
                        }
@@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                        HT_RX_REORDER_BUF_TIMEOUT))
                                goto set_release_timer;
 
+                       /* don't leave incomplete A-MSDUs around */
+                       for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
+                            i = (i + 1) % tid_agg_rx->buf_size)
+                               __skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
+
                        ht_dbg_ratelimited(sdata,
                                           "release an RX reorder frame due to timeout on earlier frames\n");
                        ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
@@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                 skipped) & IEEE80211_SN_MASK;
                        skipped = 0;
                }
-       } else while (tid_agg_rx->reorder_buf[index]) {
+       } else while (ieee80211_rx_reorder_ready(
+                               &tid_agg_rx->reorder_buf[index])) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
                index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (tid_agg_rx->reorder_buf[j])
+                       if (ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j]))
                                break;
                }
 
@@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
                                             struct sk_buff_head *frames)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        u16 sc = le16_to_cpu(hdr->seq_ctrl);
        u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
        u16 head_seq_num, buf_size;
@@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
        index = mpdu_seq_num % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
-       if (tid_agg_rx->reorder_buf[index]) {
+       if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
                dev_kfree_skb(skb);
                goto out;
        }
@@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
         */
        if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
            tid_agg_rx->stored_mpdu_num == 0) {
-               tid_agg_rx->head_seq_num =
-                       ieee80211_sn_inc(tid_agg_rx->head_seq_num);
+               if (!(status->flag & RX_FLAG_AMSDU_MORE))
+                       tid_agg_rx->head_seq_num =
+                               ieee80211_sn_inc(tid_agg_rx->head_seq_num);
                ret = false;
                goto out;
        }
 
        /* put the frame in the reordering buffer */
-       tid_agg_rx->reorder_buf[index] = skb;
-       tid_agg_rx->reorder_time[index] = jiffies;
-       tid_agg_rx->stored_mpdu_num++;
-       ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
+       if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
+               tid_agg_rx->reorder_time[index] = jiffies;
+               tid_agg_rx->stored_mpdu_num++;
+               ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       }
 
  out:
        spin_unlock(&tid_agg_rx->reorder_lock);
@@ -3129,6 +3148,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
                        if (!ieee80211_is_beacon(hdr->frame_control))
                                return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+               } else if (!ieee80211_has_tods(hdr->frame_control)) {
+                       /* ignore data frames to TDLS-peers */
+                       if (ieee80211_is_data(hdr->frame_control))
+                               return false;
+                       /* ignore action frames to TDLS-peers */
+                       if (ieee80211_is_action(hdr->frame_control) &&
+                           !ether_addr_equal(bssid, hdr->addr1))
+                               return false;
                }
                break;
        case NL80211_IFTYPE_WDS:
index 2a04361b2162308358dc5f06796b94fd34aa5c29..d411bcc8ef085215b8369fc0f066a49fd65fc6eb 100644 (file)
@@ -47,6 +47,8 @@
  * @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
  * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
  *     packets. This means the link is enabled.
+ * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
+ *     station.
  * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
  *     keeping station in power-save mode, reply when the driver
  *     unblocks the station.
@@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_PSPOLL,
        WLAN_STA_TDLS_PEER,
        WLAN_STA_TDLS_PEER_AUTH,
+       WLAN_STA_TDLS_INITIATOR,
        WLAN_STA_UAPSD,
        WLAN_STA_SP,
        WLAN_STA_4ADDR_EVENT,
@@ -152,7 +155,8 @@ struct tid_ampdu_tx {
 /**
  * struct tid_ampdu_rx - TID aggregation information (Rx).
  *
- * @reorder_buf: buffer to reorder incoming aggregated MPDUs
+ * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
+ *     A-MSDU with individually reported subframes.
  * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
@@ -177,7 +181,7 @@ struct tid_ampdu_tx {
 struct tid_ampdu_rx {
        struct rcu_head rcu_head;
        spinlock_t reorder_lock;
-       struct sk_buff **reorder_buf;
+       struct sk_buff_head *reorder_buf;
        unsigned long *reorder_time;
        struct timer_list session_timer;
        struct timer_list reorder_timer;
index f7185338a0fad33508f17837041cbdb2c2d90d68..1b21050be174b29539a954991d0376dab76fc78f 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/ieee80211.h>
+#include <linux/log2.h>
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -21,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
        struct ieee80211_local *local;
 
        sdata = container_of(wk, struct ieee80211_sub_if_data,
-                            tdls_peer_del_work.work);
+                            u.mgd.tdls_peer_del_work.work);
        local = sdata->local;
 
        mutex_lock(&local->mtx);
-       if (!is_zero_ether_addr(sdata->tdls_peer)) {
-               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
-               sta_info_destroy_addr(sdata, sdata->tdls_peer);
-               eth_zero_addr(sdata->tdls_peer);
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
+               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
+               sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
        }
        mutex_unlock(&local->mtx);
 }
@@ -46,11 +47,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
        *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
 }
 
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+                                       u16 status_code)
 {
        struct ieee80211_local *local = sdata->local;
        u16 capab;
 
+       /* The capability will be 0 when sending a failure code */
+       if (status_code != 0)
+               return 0;
+
        capab = 0;
        if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
                return capab;
@@ -63,19 +69,332 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
        return capab;
 }
 
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
-                                      const u8 *peer, const u8 *bssid)
+static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+                                      struct sk_buff *skb, const u8 *peer,
+                                      bool initiator)
 {
        struct ieee80211_tdls_lnkie *lnkid;
+       const u8 *init_addr, *rsp_addr;
+
+       if (initiator) {
+               init_addr = sdata->vif.addr;
+               rsp_addr = peer;
+       } else {
+               init_addr = peer;
+               rsp_addr = sdata->vif.addr;
+       }
 
        lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
 
        lnkid->ie_type = WLAN_EID_LINK_ID;
        lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
 
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+       memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
+{
+       switch (ac) {
+       default:
+               WARN_ON_ONCE(1);
+       case 0:
+               return IEEE80211_AC_BE;
+       case 1:
+               return IEEE80211_AC_BK;
+       case 2:
+               return IEEE80211_AC_VI;
+       case 3:
+               return IEEE80211_AC_VO;
+       }
+}
+
+static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
+{
+       u8 ret;
+
+       ret = aifsn & 0x0f;
+       if (acm)
+               ret |= 0x10;
+       ret |= (aci << 5) & 0x60;
+       return ret;
+}
+
+static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
+{
+       return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
+              ((ilog2(cw_max + 1) << 0x4) & 0xf0);
+}
+
+static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
+                                           struct sk_buff *skb)
+{
+       struct ieee80211_wmm_param_ie *wmm;
+       struct ieee80211_tx_queue_params *txq;
+       int i;
+
+       wmm = (void *)skb_put(skb, sizeof(*wmm));
+       memset(wmm, 0, sizeof(*wmm));
+
+       wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+       wmm->len = sizeof(*wmm) - 2;
+
+       wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+       wmm->oui[1] = 0x50;
+       wmm->oui[2] = 0xf2;
+       wmm->oui_type = 2; /* WME */
+       wmm->oui_subtype = 1; /* WME param */
+       wmm->version = 1; /* WME ver */
+       wmm->qos_info = 0; /* U-APSD not in use */
+
+       /*
+        * Use the EDCA parameters defined for the BSS, or default if the AP
+        * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+        */
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
+               wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
+                                                              txq->acm, i);
+               wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
+               wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
+       }
+}
+
+static void
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, bool initiator,
+                                  const u8 *extra_ies, size_t extra_ies_len)
+{
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_ht_cap ht_cap;
+       struct sta_info *sta = NULL;
+       size_t offset = 0, noffset;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       /* we should have the peer STA if we're already responding */
+       if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+               sta = sta_info_get(sdata, peer);
+               if (WARN_ON_ONCE(!sta)) {
+                       rcu_read_unlock();
+                       return;
+               }
+       }
+
+       ieee80211_add_srates_ie(sdata, skb, false, band);
+       ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+
+       /* add any custom IEs that go before Extended Capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ext_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ext_cap,
+                                            ARRAY_SIZE(before_ext_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       ieee80211_tdls_add_ext_capab(skb);
+
+       /* add the QoS element if we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+               ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
+
+       /* add any custom IEs that go before HT capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ht_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_cap,
+                                            ARRAY_SIZE(before_ht_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /*
+        * with TDLS we can switch channels, and HT-caps are not necessarily
+        * the same on all bands. The specification limits the setup to a
+        * single HT-cap, so use the current band for now.
+        */
+       sband = local->hw.wiphy->bands[band];
+       memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+       if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+            action_code == WLAN_TDLS_SETUP_RESPONSE) &&
+           ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
+               if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+                       ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+                       /* disable SMPS in TDLS initiator */
+                       ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
+                                      << IEEE80211_HT_CAP_SM_PS_SHIFT);
+               } else {
+                       /* disable SMPS in TDLS responder */
+                       sta->sta.ht_cap.cap |=
+                               (WLAN_HT_CAP_SM_PS_DISABLED
+                                << IEEE80211_HT_CAP_SM_PS_SHIFT);
+
+                       /* the peer caps are already intersected with our own */
+                       memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
+               }
+
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb, const u8 *peer,
+                                bool initiator, const u8 *extra_ies,
+                                size_t extra_ies_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       size_t offset = 0, noffset;
+       struct sta_info *sta, *ap_sta;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       sta = sta_info_get(sdata, peer);
+       ap_sta = sta_info_get(sdata, ifmgd->bssid);
+       if (WARN_ON_ONCE(!sta || !ap_sta)) {
+               rcu_read_unlock();
+               return;
+       }
+
+       /* add any custom IEs that go before the QoS IE */
+       if (extra_ies_len) {
+               static const u8 before_qos[] = {
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_qos,
+                                            ARRAY_SIZE(before_qos),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* add the QoS param IE if both the peer and we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           test_sta_flag(sta, WLAN_STA_WME))
+               ieee80211_tdls_add_wmm_param_ie(sdata, skb);
+
+       /* add any custom IEs that go before HT operation */
+       if (extra_ies_len) {
+               static const u8 before_ht_op[] = {
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_op,
+                                            ARRAY_SIZE(before_ht_op),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* if HT support is only added in TDLS, we need an HT-operation IE */
+       if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+               struct ieee80211_chanctx_conf *chanctx_conf =
+                               rcu_dereference(sdata->vif.chanctx_conf);
+               if (!WARN_ON(!chanctx_conf)) {
+                       pos = skb_put(skb, 2 +
+                                     sizeof(struct ieee80211_ht_operation));
+                       /* send an empty HT operation IE */
+                       ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
+                                                  &chanctx_conf->def, 0);
+               }
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, u16 status_code,
+                                  bool initiator, const u8 *extra_ies,
+                                  size_t extra_ies_len)
+{
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+                                                          action_code,
+                                                          initiator,
+                                                          extra_ies,
+                                                          extra_ies_len);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+                                                        initiator, extra_ies,
+                                                        extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               if (extra_ies_len)
+                       memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                              extra_ies_len);
+               if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+                       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+               break;
+       }
+
 }
 
 static int
@@ -84,7 +403,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                               u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_tdls_data *tf;
 
        tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -102,11 +420,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                skb_put(skb, sizeof(tf->u.setup_req));
                tf->u.setup_req.dialog_token = dialog_token;
                tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -116,11 +431,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                tf->u.setup_resp.status_code = cpu_to_le16(status_code);
                tf->u.setup_resp.dialog_token = dialog_token;
                tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -157,7 +469,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                           u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_mgmt *mgmt;
 
        mgmt = (void *)skb_put(skb, 24);
@@ -178,11 +489,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                mgmt->u.action.u.tdls_discover_resp.dialog_token =
                        dialog_token;
                mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        default:
                return -EINVAL;
@@ -202,7 +510,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb = NULL;
        bool send_direct;
-       const u8 *init_addr, *rsp_addr;
+       struct sta_info *sta;
        int ret;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
@@ -210,6 +518,9 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
                                sizeof(struct ieee80211_tdls_data)) +
                            50 + /* supported rates */
                            7 + /* ext capab */
+                           26 + /* max(WMM-info, WMM-param) */
+                           2 + max(sizeof(struct ieee80211_ht_cap),
+                                   sizeof(struct ieee80211_ht_operation)) +
                            extra_ies_len +
                            sizeof(struct ieee80211_tdls_lnkie));
        if (!skb)
@@ -242,45 +553,48 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto fail;
 
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
 
-       /* sanity check for initiator */
+       /* infer the initiator if we can, to support old userspace */
        switch (action_code) {
        case WLAN_TDLS_SETUP_REQUEST:
+               if (sta)
+                       set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_TDLS_SETUP_CONFIRM:
        case WLAN_TDLS_DISCOVERY_REQUEST:
-               if (!initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               initiator = true;
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
+               /*
+                * In some testing scenarios, we send a request and response.
+                * Make the last packet sent take effect for the initiator
+                * value.
+                */
+               if (sta)
+                       clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               if (initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               initiator = false;
                break;
        case WLAN_TDLS_TEARDOWN:
                /* any value is ok */
                break;
        default:
                ret = -ENOTSUPP;
-               goto fail;
+               break;
        }
 
-       if (initiator) {
-               init_addr = sdata->vif.addr;
-               rsp_addr = peer;
-       } else {
-               init_addr = peer;
-               rsp_addr = sdata->vif.addr;
-       }
+       if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
+               initiator = true;
 
-       ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
-                                  sdata->u.mgd.bssid);
+       rcu_read_unlock();
+       if (ret < 0)
+               goto fail;
 
+       ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+                              initiator, extra_ies, extra_ies_len);
        if (send_direct) {
                ieee80211_tx_skb(sdata, skb);
                return 0;
@@ -327,8 +641,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        mutex_lock(&local->mtx);
 
        /* we don't support concurrent TDLS peer setups */
-       if (!is_zero_ether_addr(sdata->tdls_peer) &&
-           !ether_addr_equal(sdata->tdls_peer, peer)) {
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
+           !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
                ret = -EBUSY;
                goto exit;
        }
@@ -336,15 +650,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        /*
         * make sure we have a STA representing the peer so we drop or buffer
         * non-TDLS-setup frames to the peer. We can't send other packets
-        * during setup through the AP path
+        * during setup through the AP path.
+        * Allow error packets to be sent - sometimes we don't even add a STA
+        * before failing the setup.
         */
-       rcu_read_lock();
-       if (!sta_info_get(sdata, peer)) {
+       if (status_code == 0) {
+               rcu_read_lock();
+               if (!sta_info_get(sdata, peer)) {
+                       rcu_read_unlock();
+                       ret = -ENOLINK;
+                       goto exit;
+               }
                rcu_read_unlock();
-               ret = -ENOLINK;
-               goto exit;
        }
-       rcu_read_unlock();
 
        ieee80211_flush_queues(local, sdata);
 
@@ -355,9 +673,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto exit;
 
-       memcpy(sdata->tdls_peer, peer, ETH_ALEN);
+       memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
        ieee80211_queue_delayed_work(&sdata->local->hw,
-                                    &sdata->tdls_peer_del_work,
+                                    &sdata->u.mgd.tdls_peer_del_work,
                                     TDLS_PEER_SETUP_TIMEOUT);
 
 exit:
@@ -513,11 +831,22 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
                rcu_read_unlock();
 
-               WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
-                            !ether_addr_equal(sdata->tdls_peer, peer));
+               WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
+                            !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
                ret = 0;
                break;
        case NL80211_TDLS_DISABLE_LINK:
+               /*
+                * The teardown message in ieee80211_tdls_mgmt_teardown() was
+                * created while the queues were stopped, so it might still be
+                * pending. Before flushing the queues we need to be sure the
+                * message is handled by the tasklet handling pending messages,
+                * otherwise we might start destroying the station before
+                * sending the teardown packet.
+                * Note that this only forces the tasklet to flush pendings -
+                * not to stop the tasklet from rescheduling itself.
+                */
+               tasklet_kill(&local->tx_pending_tasklet);
                /* flush a potentially queued teardown packet */
                ieee80211_flush_queues(local, sdata);
 
@@ -528,9 +857,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                break;
        }
 
-       if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
-               cancel_delayed_work(&sdata->tdls_peer_del_work);
-               eth_zero_addr(sdata->tdls_peer);
+       if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
+               cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
        }
 
        mutex_unlock(&local->mtx);
index ea79668c2e5ff67844470e98149dd6fd9edb0e4c..725af7a468d233b502a6de8a07e30d7a25d60e66 100644 (file)
@@ -1150,11 +1150,12 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        int err;
 
        /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN +
+                           24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN);
        if (!skb)
                return;
 
-       skb_reserve(skb, local->hw.extra_tx_headroom);
+       skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);
 
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
        memset(mgmt, 0, 24 + 6);
@@ -3082,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
 
        return max_num_different_channels;
 }
+
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
+{
+       *buf++ = WLAN_EID_VENDOR_SPECIFIC;
+       *buf++ = 7; /* len */
+       *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
+       *buf++ = 0x50;
+       *buf++ = 0xf2;
+       *buf++ = 2; /* WME */
+       *buf++ = 0; /* WME info */
+       *buf++ = 1; /* WME ver */
+       *buf++ = qosinfo; /* U-APSD no in use */
+
+       return buf;
+}
index 9265adfdabfcf99acbdf1598067884188c500a49..671ce0d27a80b01bfa5d457ef6134a19d45ee208 100644 (file)
@@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        if (!vht_cap_ie || !sband->vht_cap.vht_supported)
                return;
 
+       /* don't support VHT for TDLS peers for now */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               return;
+
        /*
         * A VHT STA must support 40 MHz, but if we verify that here
         * then we break a few things - some APs (e.g. Netgear R6300v2
index 9b3dcc201145dd3942bf30771e1638ea973759f7..f7d4ca4c46e0bf68c6232d339d4b54f7934798ba 100644 (file)
@@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 ieee80211_rx_result
 ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
 {
-       if (rx->sta->cipher_scheme)
+       if (rx->sta && rx->sta->cipher_scheme)
                return ieee80211_crypto_cs_decrypt(rx);
 
        return RX_DROP_UNUSABLE;
index 405f3c4cf70ca3617a4101e1bad278b93d7ae1b7..29c8675f9a1189db65c185f2ad04f96a67702989 100644 (file)
@@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB
          and includes code to query that database.  This is an alternative
          to using CRDA for defining regulatory rules for the kernel.
 
+         Using this option requires some parsing of the db.txt at build time,
+         the parser will be upkept with the latest wireless-regdb updates but
+         older wireless-regdb formats will be ignored. The parser may later
+         be replaced to avoid issues with conflicts on versions of
+         wireless-regdb.
+
          For details see:
 
          http://wireless.kernel.org/en/developers/Regulatory
index e9afbf10e756bd3a1ec81a6b6b7aa229d0688be7..7e3a3cef7df93b4c6936515f05f91d2ec14446ea 100644 (file)
@@ -424,7 +424,7 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
        if (end >= start)
                return jiffies_to_msecs(end - start);
 
-       return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
+       return jiffies_to_msecs(end + (ULONG_MAX - start) + 1);
 }
 
 void
index 40c37fc5b67cb76325dcd4f0041c1ad3cf5ebf66..baf2426b555a31c7aeb2fbaa48205c9b422f0cbf 100644 (file)
@@ -51,32 +51,41 @@ function parse_country_head() {
 
 function parse_reg_rule()
 {
+       flag_starts_at = 7
+
        start = $1
        sub(/\(/, "", start)
        end = $3
        bw = $5
        sub(/\),/, "", bw)
-       gain = $6
-       sub(/\(/, "", gain)
-       sub(/,/, "", gain)
-       power = $7
-       sub(/\)/, "", power)
-       sub(/,/, "", power)
+       gain = 0
+       power = $6
        # power might be in mW...
-       units = $8
+       units = $7
+       dfs_cac = 0
+
+       sub(/\(/, "", power)
+       sub(/\),/, "", power)
+       sub(/\),/, "", units)
        sub(/\)/, "", units)
-       sub(/,/, "", units)
-       dfs_cac = $9
+
        if (units == "mW") {
+               flag_starts_at = 8
                power = 10 * log(power)/log(10)
+               if ($8 ~ /[[:digit:]]/) {
+                       flag_starts_at = 9
+                       dfs_cac = $8
+               }
        } else {
-               dfs_cac = $8
+               if ($7 ~ /[[:digit:]]/) {
+                       flag_starts_at = 8
+                       dfs_cac = $7
+               }
        }
-       sub(/,/, "", dfs_cac)
        sub(/\(/, "", dfs_cac)
-       sub(/\)/, "", dfs_cac)
+       sub(/\),/, "", dfs_cac)
        flagstr = ""
-       for (i=8; i<=NF; i++)
+       for (i=flag_starts_at; i<=NF; i++)
                flagstr = flagstr $i
        split(flagstr, flagarray, ",")
        flags = ""
index c10295138eb5c64fca5c6321ada8e0e1195b3530..df7b1332a1ec2fa80834b94c233f38a427a0f350 100644 (file)
@@ -1498,18 +1498,17 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                }
                CMD(start_p2p_device, START_P2P_DEVICE);
                CMD(set_mcast_rate, SET_MCAST_RATE);
+#ifdef CONFIG_NL80211_TESTMODE
+               CMD(testmode_cmd, TESTMODE);
+#endif
                if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
                        if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
                                CMD(channel_switch, CHANNEL_SWITCH);
+                       CMD(set_qos_map, SET_QOS_MAP);
                }
-               CMD(set_qos_map, SET_QOS_MAP);
-
-#ifdef CONFIG_NL80211_TESTMODE
-               CMD(testmode_cmd, TESTMODE);
-#endif
-
+               /* add into the if now */
 #undef CMD
 
                if (rdev->ops->connect || rdev->ops->auth) {
@@ -3815,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
 {
        if (params->listen_interval != -1)
                return -EINVAL;
-       if (params->aid)
+       if (params->aid &&
+           !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                return -EINVAL;
 
        /* When you run into this, adjust the code below for the new flag */
index 558b0e3a02d8284c49de58d14833c13b444db5a2..1afdf45db38f216bb750a905dcdb5a85ae7d0897 100644 (file)
@@ -935,7 +935,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(5));
+               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
 
                if (band_rule_found && bw_fits)
                        return rr;
@@ -1019,10 +1019,10 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
 }
 #endif
 
-/* Find an ieee80211_reg_rule such that a 5MHz channel with frequency
- * chan->center_freq fits there.
- * If there is no such reg_rule, disable the channel, otherwise set the
- * flags corresponding to the bandwidths allowed in the particular reg_rule
+/*
+ * Note that right now we assume the desired channel bandwidth
+ * is always 20 MHz for each individual channel (HT40 uses 20 MHz
+ * per channel, the primary and the extension channel).
  */
 static void handle_channel(struct wiphy *wiphy,
                           enum nl80211_reg_initiator initiator,
@@ -1083,12 +1083,8 @@ static void handle_channel(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
-       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
-               bw_flags = IEEE80211_CHAN_NO_10MHZ;
-       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
-               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags |= IEEE80211_CHAN_NO_HT40;
+               bw_flags = IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1522,12 +1518,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
-       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
-               bw_flags = IEEE80211_CHAN_NO_10MHZ;
-       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
-               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags |= IEEE80211_CHAN_NO_HT40;
+               bw_flags = IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))