Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 28 Aug 2013 14:36:09 +0000 (10:36 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 28 Aug 2013 14:36:09 +0000 (10:36 -0400)
Conflicts:
drivers/net/wireless/iwlwifi/pcie/trans.c
net/mac80211/ibss.c

334 files changed:
Documentation/DocBook/80211.tmpl
MAINTAINERS
drivers/bcma/Kconfig
drivers/bcma/driver_pci.c
drivers/bcma/driver_pci_host.c
drivers/bcma/main.c
drivers/bcma/scan.c
drivers/bluetooth/btmrvl_debugfs.c
drivers/bluetooth/btmrvl_sdio.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/bmi.h
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/hif.h
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htt.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/debug.c
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath5k/pcu.c
drivers/net/wireless/ath/ath5k/qcu.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/testmode.c
drivers/net/wireless/ath/ath6kl/testmode.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/antenna.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9002_phy.h
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/common.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/phy.h
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/Makefile
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/trace.h
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/txrx.h
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/brcm80211/brcmfmac/fwil_types.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/aiutils.c
drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
drivers/net/wireless/brcm80211/brcmsmac/dma.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
drivers/net/wireless/cw1200/wsm.h
drivers/net/wireless/hostap/hostap_main.c
drivers/net/wireless/iwlegacy/3945-rs.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965-rs.c
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/constants.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/power_legacy.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/sta_rx.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800lib.h
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8180/grf5101.c
drivers/net/wireless/rtl818x/rtl8180/grf5101.h
drivers/net/wireless/rtl818x/rtl8180/max2820.c
drivers/net/wireless/rtl818x/rtl8180/max2820.h
drivers/net/wireless/rtl818x/rtl8180/rtl8225.c
drivers/net/wireless/rtl818x/rtl8180/sa2400.c
drivers/net/wireless/rtl818x/rtl8180/sa2400.h
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/net/wireless/rtl818x/rtl8187/rtl8187.h
drivers/net/wireless/rtl818x/rtl8187/rtl8225.c
drivers/net/wireless/rtl818x/rtl8187/rtl8225.h
drivers/net/wireless/rtl818x/rtl818x.h
drivers/net/wireless/rtlwifi/ps.c
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
drivers/net/wireless/rtlwifi/rtl8192cu/sw.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/testmode.c
drivers/net/wireless/ti/wlcore/testmode.h
drivers/net/wireless/zd1201.c
drivers/nfc/nfcsim.c
drivers/nfc/pn533.c
drivers/nfc/pn544/i2c.c
drivers/nfc/pn544/mei.c
drivers/nfc/pn544/pn544.c
drivers/nfc/pn544/pn544.h
drivers/ssb/Kconfig
drivers/ssb/driver_chipcommon_sflash.c
drivers/staging/rtl8187se/ieee80211/ieee80211.h
drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c
drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c
drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c
drivers/staging/rtl8187se/ieee80211/ieee80211_tx.c
drivers/staging/rtl8187se/r8180.h
drivers/staging/rtl8187se/r8180_93cx6.h
drivers/staging/rtl8187se/r8180_core.c
drivers/staging/rtl8187se/r8180_hw.h
drivers/staging/rtl8187se/r8180_rtl8225.h
drivers/staging/rtl8187se/r8180_rtl8225z2.c
drivers/staging/rtl8187se/r8180_wx.c
drivers/staging/rtl8187se/r8180_wx.h
drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h
drivers/staging/rtl8192e/rtl8192e/rtl_cam.c
drivers/staging/rtl8192e/rtl8192e/rtl_cam.h
drivers/staging/rtl8192e/rtl8192e/rtl_core.c
drivers/staging/rtl8192e/rtl8192e/rtl_core.h
drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c
drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h
drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c
drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
drivers/staging/rtl8192e/rtl8192e/rtl_pci.h
drivers/staging/rtl8192e/rtl8192e/rtl_ps.c
drivers/staging/rtl8192e/rtl8192e/rtl_ps.h
drivers/staging/rtl8192e/rtllib.h
drivers/staging/rtl8192e/rtllib_debug.h
drivers/staging/rtl8192e/rtllib_rx.c
drivers/staging/rtl8192e/rtllib_softmac.c
drivers/staging/rtl8192e/rtllib_softmac_wx.c
drivers/staging/rtl8192e/rtllib_tx.c
drivers/staging/rtl8192u/authors
drivers/staging/rtl8192u/ieee80211/ieee80211.h
drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c
drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c
drivers/staging/rtl8192u/r8180_93cx6.c
drivers/staging/rtl8192u/r8180_93cx6.h
drivers/staging/rtl8192u/r8180_pm.c
drivers/staging/rtl8192u/r8180_pm.h
drivers/staging/rtl8192u/r8190_rtl8256.h
drivers/staging/rtl8192u/r8192U.h
drivers/staging/rtl8192u/r8192U_core.c
drivers/staging/rtl8192u/r8192U_hw.h
drivers/staging/rtl8192u/r8192U_wx.c
drivers/staging/rtl8192u/r8192U_wx.h
include/linux/bcma/bcma.h
include/linux/bcma/bcma_driver_pci.h
include/linux/ieee80211.h
include/linux/platform_data/brcmfmac-sdio.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/sco.h
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
include/net/nfc/nfc.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/rfcomm/tty.c
net/bluetooth/sco.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/led.c
net/mac80211/led.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_pid_algo.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/status.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/netlink.c
net/nfc/nfc.h
net/rfkill/rfkill-regulator.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/scan.c
net/wireless/trace.h
net/wireless/util.c

index 49267ea..f403ec3 100644 (file)
           <title>functions/definitions</title>
 !Finclude/net/mac80211.h ieee80211_rx_status
 !Finclude/net/mac80211.h mac80211_rx_flags
+!Finclude/net/mac80211.h mac80211_tx_info_flags
 !Finclude/net/mac80211.h mac80211_tx_control_flags
 !Finclude/net/mac80211.h mac80211_rate_control_flags
 !Finclude/net/mac80211.h ieee80211_tx_rate
index 6447bec..2116b9a 100644 (file)
@@ -4362,7 +4362,7 @@ F:        drivers/net/wireless/iwlegacy/
 
 INTEL WIRELESS WIFI LINK (iwlwifi)
 M:     Johannes Berg <johannes.berg@intel.com>
-M:     Wey-Yi Guy <wey-yi.w.guy@intel.com>
+M:     Emmanuel Grumbach <emmanuel.grumbach@intel.com>
 M:     Intel Linux Wireless <ilw@linux.intel.com>
 L:     linux-wireless@vger.kernel.org
 W:     http://intellinuxwireless.org
@@ -5791,7 +5791,7 @@ M:        Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
 M:     Samuel Ortiz <sameo@linux.intel.com>
 L:     linux-wireless@vger.kernel.org
 L:     linux-nfc@lists.01.org (moderated for non-subscribers)
-S:     Maintained
+S:     Supported
 F:     net/nfc/
 F:     include/net/nfc/
 F:     include/uapi/linux/nfc.h
index 380a200..7c081b3 100644 (file)
@@ -35,8 +35,14 @@ config BCMA_DRIVER_PCI_HOSTMODE
          PCI core hostmode operation (external PCI bus).
 
 config BCMA_HOST_SOC
-       bool
-       depends on BCMA_DRIVER_MIPS
+       bool "Support for BCMA in a SoC"
+       depends on BCMA
+       help
+         Host interface for a Broadcom AIX bus directly mapped into
+         the memory. This only works with the Broadcom SoCs from the
+         BCM47XX line.
+
+         If unsure, say N
 
 config BCMA_DRIVER_MIPS
        bool "BCMA Broadcom MIPS core driver"
index cf7a476..c9fd694 100644 (file)
@@ -31,7 +31,7 @@ static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
        pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
 }
 
-static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
+static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy)
 {
        u32 v;
        int i;
@@ -55,7 +55,7 @@ static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
        }
 }
 
-static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
+static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address)
 {
        int max_retries = 10;
        u16 ret = 0;
@@ -98,7 +98,7 @@ static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
        return ret;
 }
 
-static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
+static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device,
                                u8 address, u16 data)
 {
        int max_retries = 10;
@@ -137,6 +137,13 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
 }
 
+static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device,
+                                   u8 address, u16 data)
+{
+       bcma_pcie_mdio_write(pc, device, address, data);
+       return bcma_pcie_mdio_read(pc, device, address);
+}
+
 /**************************************************
  * Workarounds.
  **************************************************/
@@ -203,6 +210,25 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
        }
 }
 
+static void bcma_core_pci_power_save(struct bcma_drv_pci *pc, bool up)
+{
+       u16 data;
+
+       if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
+               data = up ? 0x74 : 0x7C;
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+       } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
+               data = up ? 0x75 : 0x7D;
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
+               bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+                                        BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+       }
+}
+
 /**************************************************
  * Init.
  **************************************************/
@@ -262,7 +288,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
 
-void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
+static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
 {
        u32 w;
 
@@ -274,4 +300,33 @@ void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
        bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
        bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 }
-EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);
+
+void bcma_core_pci_up(struct bcma_bus *bus)
+{
+       struct bcma_drv_pci *pc;
+
+       if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+               return;
+
+       pc = &bus->drv_pci[0];
+
+       bcma_core_pci_power_save(pc, true);
+
+       bcma_core_pci_extend_L1timer(pc, true);
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_up);
+
+void bcma_core_pci_down(struct bcma_bus *bus)
+{
+       struct bcma_drv_pci *pc;
+
+       if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+               return;
+
+       pc = &bus->drv_pci[0];
+
+       bcma_core_pci_extend_L1timer(pc, false);
+
+       bcma_core_pci_power_save(pc, false);
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_down);
index 30629a3..c3d7b03 100644 (file)
@@ -581,6 +581,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_addresses);
 int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
 {
        struct bcma_drv_pci_host *pc_host;
+       int readrq;
 
        if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
                /* This is not a device on the PCI-core bridge. */
@@ -595,6 +596,11 @@ int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
        dev->irq = bcma_core_irq(pc_host->pdev->core);
        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
 
+       readrq = pcie_get_readrq(dev);
+       if (readrq > 128) {
+               pr_info("change PCIe max read request size from %i to 128\n", readrq);
+               pcie_set_readrq(dev, 128);
+       }
        return 0;
 }
 EXPORT_SYMBOL(bcma_core_pci_plat_dev_init);
index 0067422..90ee350 100644 (file)
@@ -237,7 +237,7 @@ int bcma_bus_register(struct bcma_bus *bus)
        err = bcma_bus_scan(bus);
        if (err) {
                bcma_err(bus, "Failed to scan: %d\n", err);
-               return -1;
+               return err;
        }
 
        /* Early init CC core */
index 8bffa5c..cd6b20f 100644 (file)
@@ -32,6 +32,18 @@ static const struct bcma_device_id_name bcma_bcm_device_names[] = {
        { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" },
        { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" },
        { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" },
+       { BCMA_CORE_PCIEG2, "PCIe Gen 2" },
+       { BCMA_CORE_DMA, "DMA" },
+       { BCMA_CORE_SDIO3, "SDIO3" },
+       { BCMA_CORE_USB20, "USB 2.0" },
+       { BCMA_CORE_USB30, "USB 3.0" },
+       { BCMA_CORE_A9JTAG, "ARM Cortex A9 JTAG" },
+       { BCMA_CORE_DDR23, "Denali DDR2/DDR3 memory controller" },
+       { BCMA_CORE_ROM, "ROM" },
+       { BCMA_CORE_NAND, "NAND flash controller" },
+       { BCMA_CORE_QSPI, "SPI flash controller" },
+       { BCMA_CORE_CHIPCOMMON_B, "Chipcommon B" },
+       { BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" },
        { BCMA_CORE_AMEMC, "AMEMC (DDR)" },
        { BCMA_CORE_ALTA, "ALTA (I2S)" },
        { BCMA_CORE_INVALID, "Invalid" },
@@ -201,7 +213,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr)
        return ent;
 }
 
-static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
+static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
                                  u32 type, u8 port)
 {
        u32 addrl, addrh, sizel, sizeh = 0;
@@ -213,7 +225,7 @@ static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
            ((ent & SCAN_ADDR_TYPE) != type) ||
            (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
                bcma_erom_push_ent(eromptr);
-               return -EINVAL;
+               return (u32)-EINVAL;
        }
 
        addrl = ent & SCAN_ADDR_ADDR;
@@ -261,7 +273,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                              struct bcma_device_id *match, int core_num,
                              struct bcma_device *core)
 {
-       s32 tmp;
+       u32 tmp;
        u8 i, j;
        s32 cia, cib;
        u8 ports[2], wrappers[2];
@@ -339,11 +351,11 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
         * the main register space for the core
         */
        tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
-       if (tmp <= 0) {
+       if (tmp == 0 || IS_ERR_VALUE(tmp)) {
                /* Try again to see if it is a bridge */
                tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                              SCAN_ADDR_TYPE_BRIDGE, 0);
-               if (tmp <= 0) {
+               if (tmp == 0 || IS_ERR_VALUE(tmp)) {
                        return -EILSEQ;
                } else {
                        bcma_info(bus, "Bridge found\n");
@@ -357,7 +369,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_SLAVE, i);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: slave port %d "
                                 * "has %d descriptors\n", i, j); */
@@ -374,7 +386,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_MWRAP, i);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: master wrapper %d "
                                 * "has %d descriptors\n", i, j); */
@@ -392,7 +404,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
                for (j = 0; ; j++) {
                        tmp = bcma_erom_get_addr_desc(bus, eromptr,
                                SCAN_ADDR_TYPE_SWRAP, i + hack);
-                       if (tmp < 0) {
+                       if (IS_ERR_VALUE(tmp)) {
                                /* no more entries for port _i_ */
                                /* pr_debug("erom: master wrapper %d "
                                 * has %d descriptors\n", i, j); */
index db2c3c3..023d35e 100644 (file)
@@ -43,7 +43,7 @@ static ssize_t btmrvl_hscfgcmd_write(struct file *file,
        if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
 
-       ret = strict_strtol(buf, 10, &result);
+       ret = kstrtol(buf, 10, &result);
        if (ret)
                return ret;
 
@@ -89,7 +89,7 @@ static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
        if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
 
-       ret = strict_strtol(buf, 10, &result);
+       ret = kstrtol(buf, 10, &result);
        if (ret)
                return ret;
 
@@ -135,7 +135,7 @@ static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
        if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
 
-       ret = strict_strtol(buf, 10, &result);
+       ret = kstrtol(buf, 10, &result);
        if (ret)
                return ret;
 
index 75c2626..00da6df 100644 (file)
@@ -486,7 +486,7 @@ static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
                        if (firmwarelen - offset < txlen)
                                txlen = firmwarelen - offset;
 
-                       tx_blocks = (txlen + blksz_dl - 1) / blksz_dl;
+                       tx_blocks = DIV_ROUND_UP(txlen, blksz_dl);
 
                        memcpy(fwbuf, &firmware[offset], txlen);
                }
@@ -873,7 +873,7 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
        }
 
        blksz = SDIO_BLOCK_SIZE;
-       buf_block_len = (nb + blksz - 1) / blksz;
+       buf_block_len = DIV_ROUND_UP(nb, blksz);
 
        sdio_claim_host(card->func);
 
index 1d680ba..7b839bf 100644 (file)
@@ -130,7 +130,7 @@ config BNX2X_SRIOV
 
 config BGMAC
        tristate "BCMA bus GBit core support"
-       depends on BCMA_HOST_SOC && HAS_DMA
+       depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
        ---help---
          This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
          They can be found on BCM47xx SoCs and provide gigabit ethernet.
index daeafef..e0ba7cd 100644 (file)
@@ -159,7 +159,7 @@ struct ath_common {
 
        bool btcoex_enabled;
        bool disable_ani;
-       bool antenna_diversity;
+       bool bt_ant_diversity;
 };
 
 struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
index 1a2ef51..744da6d 100644 (file)
 #include "debug.h"
 #include "htc.h"
 
+void ath10k_bmi_start(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+       ar->bmi.done_sent = false;
+}
+
 int ath10k_bmi_done(struct ath10k *ar)
 {
        struct bmi_cmd cmd;
@@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
                                                  &resp, &rxlen);
                if (ret) {
-                       ath10k_warn("unable to read from the device\n");
+                       ath10k_warn("unable to read from the device (%d)\n",
+                                   ret);
                        return ret;
                }
 
@@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
                                                  NULL, NULL);
                if (ret) {
-                       ath10k_warn("unable to write to the device\n");
+                       ath10k_warn("unable to write to the device (%d)\n",
+                                   ret);
                        return ret;
                }
 
index 32c56aa..8d81ce1 100644 (file)
@@ -184,6 +184,7 @@ struct bmi_target_info {
 #define BMI_CE_NUM_TO_TARG 0
 #define BMI_CE_NUM_TO_HOST 1
 
+void ath10k_bmi_start(struct ath10k *ar);
 int ath10k_bmi_done(struct ath10k *ar);
 int ath10k_bmi_get_target_info(struct ath10k *ar,
                               struct bmi_target_info *target_info);
index 61a8ac7..f8b969f 100644 (file)
@@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *indicator_addr;
 
-       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
                ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
                return;
        }
@@ -637,6 +637,7 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
                ath10k_pci_wake(ar);
                src_ring->hw_index =
                        ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               src_ring->hw_index &= nentries_mask;
                ath10k_pci_sleep(ar);
        }
        read_index = src_ring->hw_index;
@@ -950,10 +951,12 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
 
        ath10k_pci_wake(ar);
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       src_ring->sw_index &= src_ring->nentries_mask;
        src_ring->hw_index = src_ring->sw_index;
 
        src_ring->write_index =
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+       src_ring->write_index &= src_ring->nentries_mask;
        ath10k_pci_sleep(ar);
 
        src_ring->per_transfer_context = (void **)ptr;
@@ -1035,8 +1038,10 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
 
        ath10k_pci_wake(ar);
        dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->sw_index &= dest_ring->nentries_mask;
        dest_ring->write_index =
                ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       dest_ring->write_index &= dest_ring->nentries_mask;
        ath10k_pci_sleep(ar);
 
        dest_ring->per_transfer_context = (void **)ptr;
index 2b3426b..7226c23 100644 (file)
@@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
                goto conn_fail;
 
        /* Start HTC */
-       status = ath10k_htc_start(ar->htc);
+       status = ath10k_htc_start(&ar->htc);
        if (status)
                goto conn_fail;
 
@@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
        return 0;
 
 timeout:
-       ath10k_htc_stop(ar->htc);
+       ath10k_htc_stop(&ar->htc);
 conn_fail:
        return status;
 }
@@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
 
 static int ath10k_download_board_data(struct ath10k *ar)
 {
+       const struct firmware *fw = ar->board_data;
        u32 board_data_size = QCA988X_BOARD_DATA_SZ;
        u32 address;
-       const struct firmware *fw;
        int ret;
 
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.board);
-       if (IS_ERR(fw)) {
-               ath10k_err("could not fetch board data fw file (%ld)\n",
-                          PTR_ERR(fw));
-               return PTR_ERR(fw);
-       }
-
        ret = ath10k_push_board_ext_data(ar, fw);
        if (ret) {
                ath10k_err("could not push board ext data (%d)\n", ret);
@@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
        return ret;
 }
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       const struct firmware *fw;
-       u32 address;
+       const struct firmware *fw = ar->otp;
+       u32 address = ar->hw_params.patch_load_addr;
        u32 exec_param;
        int ret;
 
        /* OTP is optional */
 
-       if (ar->hw_params.fw.otp == NULL) {
-               ath10k_info("otp file not defined\n");
-               return 0;
-       }
-
-       address = ar->hw_params.patch_load_addr;
-
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.otp);
-       if (IS_ERR(fw)) {
-               ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+       if (!ar->otp)
                return 0;
-       }
 
        ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
        if (ret) {
@@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
        return ret;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
 {
-       const struct firmware *fw;
+       const struct firmware *fw = ar->firmware;
        u32 address;
        int ret;
 
-       if (ar->hw_params.fw.fw == NULL)
-               return -EINVAL;
-
        address = ar->hw_params.patch_load_addr;
 
-       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
-                                 ar->hw_params.fw.fw);
-       if (IS_ERR(fw)) {
-               ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
-               return PTR_ERR(fw);
-       }
-
        ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
        if (ret) {
                ath10k_err("could not write fw (%d)\n", ret);
@@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
        }
 
 exit:
-       release_firmware(fw);
+       return ret;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
+       if (ar->board_data && !IS_ERR(ar->board_data))
+               release_firmware(ar->board_data);
+
+       if (ar->otp && !IS_ERR(ar->otp))
+               release_firmware(ar->otp);
+
+       if (ar->firmware && !IS_ERR(ar->firmware))
+               release_firmware(ar->firmware);
+
+       ar->board_data = NULL;
+       ar->otp = NULL;
+       ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+       int ret = 0;
+
+       if (ar->hw_params.fw.fw == NULL) {
+               ath10k_err("firmware file not defined\n");
+               return -EINVAL;
+       }
+
+       if (ar->hw_params.fw.board == NULL) {
+               ath10k_err("board data file not defined");
+               return -EINVAL;
+       }
+
+       ar->board_data = ath10k_fetch_fw_file(ar,
+                                             ar->hw_params.fw.dir,
+                                             ar->hw_params.fw.board);
+       if (IS_ERR(ar->board_data)) {
+               ret = PTR_ERR(ar->board_data);
+               ath10k_err("could not fetch board data (%d)\n", ret);
+               goto err;
+       }
+
+       ar->firmware = ath10k_fetch_fw_file(ar,
+                                           ar->hw_params.fw.dir,
+                                           ar->hw_params.fw.fw);
+       if (IS_ERR(ar->firmware)) {
+               ret = PTR_ERR(ar->firmware);
+               ath10k_err("could not fetch firmware (%d)\n", ret);
+               goto err;
+       }
+
+       /* OTP may be undefined. If so, don't fetch it at all */
+       if (ar->hw_params.fw.otp == NULL)
+               return 0;
+
+       ar->otp = ath10k_fetch_fw_file(ar,
+                                      ar->hw_params.fw.dir,
+                                      ar->hw_params.fw.otp);
+       if (IS_ERR(ar->otp)) {
+               ret = PTR_ERR(ar->otp);
+               ath10k_err("could not fetch otp (%d)\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       ath10k_core_free_firmware_files(ar);
        return ret;
 }
 
@@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar)
        return 0;
 }
 
+static void ath10k_core_restart(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+       mutex_lock(&ar->conf_mutex);
+
+       switch (ar->state) {
+       case ATH10K_STATE_ON:
+               ath10k_halt(ar);
+               ar->state = ATH10K_STATE_RESTARTING;
+               ieee80211_restart_hw(ar->hw);
+               break;
+       case ATH10K_STATE_OFF:
+               /* this can happen if driver is being unloaded */
+               ath10k_warn("cannot restart a device that hasn't been started\n");
+               break;
+       case ATH10K_STATE_RESTARTING:
+       case ATH10K_STATE_RESTARTED:
+               ar->state = ATH10K_STATE_WEDGED;
+               /* fall through */
+       case ATH10K_STATE_WEDGED:
+               ath10k_warn("device is wedged, will not restart\n");
+               break;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 enum ath10k_bus bus,
                                  const struct ath10k_hif_ops *hif_ops)
 {
        struct ath10k *ar;
@@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
        ar->hif.priv = hif_priv;
        ar->hif.ops = hif_ops;
-       ar->hif.bus = bus;
-
-       ar->free_vdev_map = 0xFF; /* 8 vdevs */
 
        init_completion(&ar->scan.started);
        init_completion(&ar->scan.completed);
@@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 
        init_waitqueue_head(&ar->event_queue);
 
+       INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
        return ar;
 
 err_wq:
@@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_destroy);
 
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
 {
-       struct ath10k_htc_ops htc_ops;
-       struct bmi_target_info target_info;
        int status;
 
-       memset(&target_info, 0, sizeof(target_info));
-       status = ath10k_bmi_get_target_info(ar, &target_info);
-       if (status)
-               goto err;
-
-       ar->target_version = target_info.version;
-       ar->hw->wiphy->hw_version = target_info.version;
-
-       status = ath10k_init_hw_params(ar);
-       if (status)
-               goto err;
+       ath10k_bmi_start(ar);
 
        if (ath10k_init_configure_target(ar)) {
                status = -EINVAL;
@@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar)
        if (status)
                goto err;
 
-       htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+       ar->htc.htc_ops.target_send_suspend_complete =
+               ath10k_send_suspend_complete;
 
-       ar->htc = ath10k_htc_create(ar, &htc_ops);
-       if (IS_ERR(ar->htc)) {
-               status = PTR_ERR(ar->htc);
-               ath10k_err("could not create HTC (%d)\n", status);
+       status = ath10k_htc_init(ar);
+       if (status) {
+               ath10k_err("could not init HTC (%d)\n", status);
                goto err;
        }
 
        status = ath10k_bmi_done(ar);
        if (status)
-               goto err_htc_destroy;
+               goto err;
 
        status = ath10k_wmi_attach(ar);
        if (status) {
                ath10k_err("WMI attach failed: %d\n", status);
-               goto err_htc_destroy;
+               goto err;
        }
 
-       status = ath10k_htc_wait_target(ar->htc);
+       status = ath10k_htc_wait_target(&ar->htc);
        if (status)
                goto err_wmi_detach;
 
-       ar->htt = ath10k_htt_attach(ar);
-       if (!ar->htt) {
-               status = -ENOMEM;
+       status = ath10k_htt_attach(ar);
+       if (status) {
+               ath10k_err("could not attach htt (%d)\n", status);
                goto err_wmi_detach;
        }
 
@@ -588,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar)
                goto err_disconnect_htc;
        }
 
-       status = ath10k_htt_attach_target(ar->htt);
-       if (status)
-               goto err_disconnect_htc;
-
-       status = ath10k_mac_register(ar);
+       status = ath10k_htt_attach_target(&ar->htt);
        if (status)
                goto err_disconnect_htc;
 
-       status = ath10k_debug_create(ar);
-       if (status) {
-               ath10k_err("unable to initialize debugfs\n");
-               goto err_unregister_mac;
-       }
+       ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
 
        return 0;
 
-err_unregister_mac:
-       ath10k_mac_unregister(ar);
 err_disconnect_htc:
-       ath10k_htc_stop(ar->htc);
+       ath10k_htc_stop(&ar->htc);
 err_htt_detach:
-       ath10k_htt_detach(ar->htt);
+       ath10k_htt_detach(&ar->htt);
 err_wmi_detach:
        ath10k_wmi_detach(ar);
-err_htc_destroy:
-       ath10k_htc_destroy(ar->htc);
 err:
        return status;
 }
-EXPORT_SYMBOL(ath10k_core_register);
+EXPORT_SYMBOL(ath10k_core_start);
 
-void ath10k_core_unregister(struct ath10k *ar)
+void ath10k_core_stop(struct ath10k *ar)
 {
-       /* We must unregister from mac80211 before we stop HTC and HIF.
-        * Otherwise we will fail to submit commands to FW and mac80211 will be
-        * unhappy about callback failures. */
-       ath10k_mac_unregister(ar);
-       ath10k_htc_stop(ar->htc);
-       ath10k_htt_detach(ar->htt);
+       ath10k_htc_stop(&ar->htc);
+       ath10k_htt_detach(&ar->htt);
        ath10k_wmi_detach(ar);
-       ath10k_htc_destroy(ar->htc);
 }
-EXPORT_SYMBOL(ath10k_core_unregister);
+EXPORT_SYMBOL(ath10k_core_stop);
 
-int ath10k_core_target_suspend(struct ath10k *ar)
+/* mac80211 manages fw/hw initialization through start/stop hooks. However in
+ * order to know what hw capabilities should be advertised to mac80211 it is
+ * necessary to load the firmware (and tear it down immediately since start
+ * hook will try to init it again) before registering */
+static int ath10k_core_probe_fw(struct ath10k *ar)
 {
-       int ret;
+       struct bmi_target_info target_info;
+       int ret = 0;
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not start pci hif (%d)\n", ret);
+               return ret;
+       }
 
-       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+       memset(&target_info, 0, sizeof(target_info));
+       ret = ath10k_bmi_get_target_info(ar, &target_info);
+       if (ret) {
+               ath10k_err("could not get target info (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
 
-       ret = ath10k_wmi_pdev_suspend_target(ar);
-       if (ret)
-               ath10k_warn("could not suspend target (%d)\n", ret);
+       ar->target_version = target_info.version;
+       ar->hw->wiphy->hw_version = target_info.version;
 
-       return ret;
+       ret = ath10k_init_hw_params(ar);
+       if (ret) {
+               ath10k_err("could not get hw params (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ret = ath10k_core_fetch_firmware_files(ar);
+       if (ret) {
+               ath10k_err("could not fetch firmware files (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_core_free_firmware_files(ar);
+               ath10k_hif_power_down(ar);
+               return ret;
+       }
+
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+       return 0;
 }
-EXPORT_SYMBOL(ath10k_core_target_suspend);
 
-int ath10k_core_target_resume(struct ath10k *ar)
+int ath10k_core_register(struct ath10k *ar)
 {
-       int ret;
+       int status;
 
-       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+       status = ath10k_core_probe_fw(ar);
+       if (status) {
+               ath10k_err("could not probe fw (%d)\n", status);
+               return status;
+       }
 
-       ret = ath10k_wmi_pdev_resume_target(ar);
-       if (ret)
-               ath10k_warn("could not resume target (%d)\n", ret);
+       status = ath10k_mac_register(ar);
+       if (status) {
+               ath10k_err("could not register to mac80211 (%d)\n", status);
+               goto err_release_fw;
+       }
 
-       return ret;
+       status = ath10k_debug_create(ar);
+       if (status) {
+               ath10k_err("unable to initialize debugfs\n");
+               goto err_unregister_mac;
+       }
+
+       return 0;
+
+err_unregister_mac:
+       ath10k_mac_unregister(ar);
+err_release_fw:
+       ath10k_core_free_firmware_files(ar);
+       return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+       /* We must unregister from mac80211 before we stop HTC and HIF.
+        * Otherwise we will fail to submit commands to FW and mac80211 will be
+        * unhappy about callback failures. */
+       ath10k_mac_unregister(ar);
+       ath10k_core_free_firmware_files(ar);
 }
-EXPORT_SYMBOL(ath10k_core_target_resume);
+EXPORT_SYMBOL(ath10k_core_unregister);
 
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
index 539336d..e4bba56 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 
+#include "htt.h"
 #include "htc.h"
 #include "hw.h"
 #include "targaddrs.h"
 #define ATH10K_SCAN_ID 0
 #define WMI_READY_TIMEOUT (5 * HZ)
 #define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+#define ATH10K_NUM_CHANS 38
 
 /* Antenna noise floor */
 #define ATH10K_DEFAULT_NOISE_FLOOR -95
 
 struct ath10k;
 
-enum ath10k_bus {
-       ATH10K_BUS_PCI,
-};
-
 struct ath10k_skb_cb {
        dma_addr_t paddr;
        bool is_mapped;
@@ -250,6 +248,28 @@ struct ath10k_debug {
        struct completion event_stats_compl;
 };
 
+enum ath10k_state {
+       ATH10K_STATE_OFF = 0,
+       ATH10K_STATE_ON,
+
+       /* When doing firmware recovery the device is first powered down.
+        * mac80211 is supposed to call in to start() hook later on. It is
+        * however possible that driver unloading and firmware crash overlap.
+        * mac80211 can wait on conf_mutex in stop() while the device is
+        * stopped in ath10k_core_restart() work holding conf_mutex. The state
+        * RESTARTED means that the device is up and mac80211 has started hw
+        * reconfiguration. Once mac80211 is done with the reconfiguration we
+        * set the state to STATE_ON in restart_complete(). */
+       ATH10K_STATE_RESTARTING,
+       ATH10K_STATE_RESTARTED,
+
+       /* The device has crashed while restarting hw. This state is like ON
+        * but commands are blocked in HTC and -ECOMM response is given. This
+        * prevents completion timeouts and makes the driver more responsive to
+        * userspace commands. This is also prevents recursive recovery. */
+       ATH10K_STATE_WEDGED,
+};
+
 struct ath10k {
        struct ath_common ath_common;
        struct ieee80211_hw *hw;
@@ -266,6 +286,7 @@ struct ath10k {
        u32 hw_max_tx_power;
        u32 ht_cap_info;
        u32 vht_cap_info;
+       u32 num_rf_chains;
 
        struct targetdef *targetdef;
        struct hostdef *hostdef;
@@ -274,19 +295,16 @@ struct ath10k {
 
        struct {
                void *priv;
-               enum ath10k_bus bus;
                const struct ath10k_hif_ops *ops;
        } hif;
 
-       struct ath10k_wmi wmi;
-
        wait_queue_head_t event_queue;
        bool is_target_paused;
 
        struct ath10k_bmi bmi;
-
-       struct ath10k_htc *htc;
-       struct ath10k_htt *htt;
+       struct ath10k_wmi wmi;
+       struct ath10k_htc htc;
+       struct ath10k_htt htt;
 
        struct ath10k_hw_params {
                u32 id;
@@ -301,6 +319,10 @@ struct ath10k {
                } fw;
        } hw_params;
 
+       const struct firmware *board_data;
+       const struct firmware *otp;
+       const struct firmware *firmware;
+
        struct {
                struct completion started;
                struct completion completed;
@@ -350,20 +372,28 @@ struct ath10k {
        struct completion offchan_tx_completed;
        struct sk_buff *offchan_tx_skb;
 
+       enum ath10k_state state;
+
+       struct work_struct restart_work;
+
+       /* cycle count is reported twice for each visited channel during scan.
+        * access protected by data_lock */
+       u32 survey_last_rx_clear_count;
+       u32 survey_last_cycle_count;
+       struct survey_info survey[ATH10K_NUM_CHANS];
+
 #ifdef CONFIG_ATH10K_DEBUGFS
        struct ath10k_debug debug;
 #endif
 };
 
 struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 enum ath10k_bus bus,
                                  const struct ath10k_hif_ops *hif_ops);
 void ath10k_core_destroy(struct ath10k *ar);
 
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar);
 void ath10k_core_unregister(struct ath10k *ar);
 
-int ath10k_core_target_suspend(struct ath10k *ar);
-int ath10k_core_target_resume(struct ath10k *ar);
-
 #endif /* _CORE_H_ */
index 499034b..3d65594 100644 (file)
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        struct wmi_pdev_stats *ps;
        int i;
 
-       mutex_lock(&ar->conf_mutex);
+       spin_lock_bh(&ar->data_lock);
 
        stats = &ar->debug.target_stats;
 
@@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
                }
        }
 
+       spin_unlock_bh(&ar->data_lock);
        mutex_unlock(&ar->conf_mutex);
        complete(&ar->debug.event_stats_compl);
 }
@@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
 {
        struct ath10k *ar = file->private_data;
        struct ath10k_target_stats *fw_stats;
-       char *buf;
+       char *buf = NULL;
        unsigned int len = 0, buf_len = 2500;
-       ssize_t ret_cnt;
+       ssize_t ret_cnt = 0;
        long left;
        int i;
        int ret;
 
        fw_stats = &ar->debug.target_stats;
 
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_ON)
+               goto exit;
+
        buf = kzalloc(buf_len, GFP_KERNEL);
        if (!buf)
-               return -ENOMEM;
+               goto exit;
 
        ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
        if (ret) {
                ath10k_warn("could not request stats (%d)\n", ret);
-               kfree(buf);
-               return -EIO;
+               goto exit;
        }
 
        left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+       if (left <= 0)
+               goto exit;
 
-       if (left <= 0) {
-               kfree(buf);
-               return -ETIMEDOUT;
-       }
-
-       mutex_lock(&ar->conf_mutex);
-
+       spin_lock_bh(&ar->data_lock);
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s\n",
                         "ath10k PDEV stats");
@@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                                 fw_stats->peer_stat[i].peer_tx_rate);
                len += scnprintf(buf + len, buf_len - len, "\n");
        }
+       spin_unlock_bh(&ar->data_lock);
 
        if (len > buf_len)
                len = buf_len;
 
        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
+exit:
        mutex_unlock(&ar->conf_mutex);
-
        kfree(buf);
        return ret_cnt;
 }
@@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       const char buf[] = "To simulate firmware crash write the keyword"
+                          " `crash` to this file.\nThis will force firmware"
+                          " to report a crash to the host system.\n";
+       return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char buf[32] = {};
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+       if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (ar->state != ATH10K_STATE_ON &&
+           ar->state != ATH10K_STATE_RESTARTED) {
+               ret = -ENETDOWN;
+               goto exit;
+       }
+
+       ath10k_info("simulating firmware crash\n");
+
+       ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+       if (ret)
+               ath10k_warn("failed to force fw hang (%d)\n", ret);
+
+       if (ret == 0)
+               ret = count;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+       .read = ath10k_read_simulate_fw_crash,
+       .write = ath10k_write_simulate_fw_crash,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath10k_debug_create(struct ath10k *ar)
 {
        ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
@@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
                            &fops_wmi_services);
 
+       debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+                           ar, &fops_simulate_fw_crash);
+
        return 0;
 }
 #endif /* CONFIG_ATH10K_DEBUGFS */
index 73a24d4..dcdea68 100644 (file)
@@ -46,8 +46,11 @@ struct ath10k_hif_ops {
                                void *request, u32 request_len,
                                void *response, u32 *response_len);
 
+       /* Post BMI phase, after FW is loaded. Starts regular operation */
        int (*start)(struct ath10k *ar);
 
+       /* Clean up what start() did. This does not revert to BMI phase. If
+        * desired so, call power_down() and power_up() */
        void (*stop)(struct ath10k *ar);
 
        int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
@@ -66,10 +69,20 @@ struct ath10k_hif_ops {
         */
        void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
 
-       void (*init)(struct ath10k *ar,
-                    struct ath10k_hif_cb *callbacks);
+       void (*set_callbacks)(struct ath10k *ar,
+                             struct ath10k_hif_cb *callbacks);
 
        u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+       /* Power up the device and enter BMI transfer mode for FW download */
+       int (*power_up)(struct ath10k *ar);
+
+       /* Power down the device and free up resources. stop() must be called
+        * before this if start() was called earlier */
+       void (*power_down)(struct ath10k *ar);
+
+       int (*suspend)(struct ath10k *ar);
+       int (*resume)(struct ath10k *ar);
 };
 
 
@@ -122,10 +135,10 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
        ar->hif.ops->send_complete_check(ar, pipe_id, force);
 }
 
-static inline void ath10k_hif_init(struct ath10k *ar,
-                                  struct ath10k_hif_cb *callbacks)
+static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
+                                           struct ath10k_hif_cb *callbacks)
 {
-       ar->hif.ops->init(ar, callbacks);
+       ar->hif.ops->set_callbacks(ar, callbacks);
 }
 
 static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
@@ -134,4 +147,30 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
        return ar->hif.ops->get_free_queue_number(ar, pipe_id);
 }
 
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+       return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+       ar->hif.ops->power_down(ar);
+}
+
+static inline int ath10k_hif_suspend(struct ath10k *ar)
+{
+       if (!ar->hif.ops->suspend)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->suspend(ar);
+}
+
+static inline int ath10k_hif_resume(struct ath10k *ar)
+{
+       if (!ar->hif.ops->resume)
+               return -EOPNOTSUPP;
+
+       return ar->hif.ops->resume(ar);
+}
+
 #endif /* _HIF_H_ */
index 74363c9..ef3329e 100644 (file)
@@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 {
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
 
+       if (htc->ar->state == ATH10K_STATE_WEDGED)
+               return -ECOMM;
+
        if (eid >= ATH10K_HTC_EP_COUNT) {
                ath10k_warn("Invalid endpoint id: %d\n", eid);
                return -ENOENT;
        }
 
-       skb_push(skb, sizeof(struct ath10k_htc_hdr));
-
        spin_lock_bh(&htc->tx_lock);
+       if (htc->stopped) {
+               spin_unlock_bh(&htc->tx_lock);
+               return -ESHUTDOWN;
+       }
+
        __skb_queue_tail(&ep->tx_queue, skb);
+       skb_push(skb, sizeof(struct ath10k_htc_hdr));
        spin_unlock_bh(&htc->tx_lock);
 
        queue_work(htc->ar->workqueue, &ep->send_work);
@@ -265,25 +272,19 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
                                            struct sk_buff *skb,
                                            unsigned int eid)
 {
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_ep *ep = &htc->endpoint[eid];
-       bool stopping;
 
        ath10k_htc_notify_tx_completion(ep, skb);
        /* the skb now belongs to the completion handler */
 
+       /* note: when using TX credit flow, the re-checking of queues happens
+        * when credits flow back from the target.  in the non-TX credit case,
+        * we recheck after the packet completes */
        spin_lock_bh(&htc->tx_lock);
-       stopping = htc->stopping;
-       spin_unlock_bh(&htc->tx_lock);
-
-       if (!ep->tx_credit_flow_enabled && !stopping)
-               /*
-                * note: when using TX credit flow, the re-checking of
-                * queues happens when credits flow back from the target.
-                * in the non-TX credit case, we recheck after the packet
-                * completes
-                */
+       if (!ep->tx_credit_flow_enabled && !htc->stopped)
                queue_work(ar->workqueue, &ep->send_work);
+       spin_unlock_bh(&htc->tx_lock);
 
        return 0;
 }
@@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
                                            u8 pipe_id)
 {
        int status = 0;
-       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc *htc = &ar->htc;
        struct ath10k_htc_hdr *hdr;
        struct ath10k_htc_ep *ep;
        u16 payload_len;
@@ -751,8 +752,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
        tx_alloc = ath10k_htc_get_credit_allocation(htc,
                                                    conn_req->service_id);
        if (!tx_alloc)
-               ath10k_warn("HTC Service %s does not allocate target credits\n",
-                           htc_service_name(conn_req->service_id));
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC Service %s does not allocate target credits\n",
+                          htc_service_name(conn_req->service_id));
 
        skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
        if (!skb) {
@@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        struct ath10k_htc_ep *ep;
 
        spin_lock_bh(&htc->tx_lock);
-       htc->stopping = true;
+       htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
 
        for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
@@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
        }
 
        ath10k_hif_stop(htc->ar);
-       ath10k_htc_reset_endpoint_states(htc);
 }
 
 /* registered target arrival callback from the HIF layer */
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-                                    struct ath10k_htc_ops *htc_ops)
+int ath10k_htc_init(struct ath10k *ar)
 {
        struct ath10k_hif_cb htc_callbacks;
        struct ath10k_htc_ep *ep = NULL;
-       struct ath10k_htc *htc = NULL;
-
-       /* FIXME: use struct ath10k instead */
-       htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
-       if (!htc)
-               return ERR_PTR(-ENOMEM);
+       struct ath10k_htc *htc = &ar->htc;
 
        spin_lock_init(&htc->tx_lock);
 
-       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
-
+       htc->stopped = false;
        ath10k_htc_reset_endpoint_states(htc);
 
        /* setup HIF layer callbacks */
@@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
        /* Get HIF default pipe for HTC message exchange */
        ep = &htc->endpoint[ATH10K_HTC_EP_0];
 
-       ath10k_hif_init(ar, &htc_callbacks);
+       ath10k_hif_set_callbacks(ar, &htc_callbacks);
        ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
 
        init_completion(&htc->ctl_resp);
 
-       return htc;
-}
-
-void ath10k_htc_destroy(struct ath10k_htc *htc)
-{
-       kfree(htc);
+       return 0;
 }
index fa45844..e1dd8c7 100644 (file)
@@ -335,7 +335,7 @@ struct ath10k_htc {
        struct ath10k *ar;
        struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
 
-       /* protects endpoint and stopping fields */
+       /* protects endpoint and stopped fields */
        spinlock_t tx_lock;
 
        struct ath10k_htc_ops htc_ops;
@@ -349,11 +349,10 @@ struct ath10k_htc {
        struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
        int target_credit_size;
 
-       bool stopping;
+       bool stopped;
 };
 
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
-                                    struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_init(struct ath10k *ar);
 int ath10k_htc_wait_target(struct ath10k_htc *htc);
 int ath10k_htc_start(struct ath10k_htc *htc);
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
@@ -362,7 +361,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
                    struct sk_buff *packet);
 void ath10k_htc_stop(struct ath10k_htc *htc);
-void ath10k_htc_destroy(struct ath10k_htc *htc);
 struct sk_buff *ath10k_htc_alloc_skb(int size);
 
 #endif
index 185a546..39342c5 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/if_ether.h>
 
 #include "htt.h"
 #include "core.h"
@@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        /* connect to control service */
        conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
 
-       status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+       status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
                                            &conn_resp);
 
        if (status)
@@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_attach(struct ath10k *ar)
 {
-       struct ath10k_htt *htt;
+       struct ath10k_htt *htt = &ar->htt;
        int ret;
 
-       htt = kzalloc(sizeof(*htt), GFP_KERNEL);
-       if (!htt)
-               return NULL;
-
        htt->ar = ar;
        htt->max_throughput_mbps = 800;
 
@@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
         * since ath10k_htt_rx_attach involves sending a rx ring configure
         * message to the target.
         */
-       if (ath10k_htt_htc_attach(htt))
+       ret = ath10k_htt_htc_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt htc (%d)\n", ret);
                goto err_htc_attach;
+       }
 
        ret = ath10k_htt_tx_attach(htt);
        if (ret) {
@@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
                goto err_htc_attach;
        }
 
-       if (ath10k_htt_rx_attach(htt))
+       ret = ath10k_htt_rx_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt rx (%d)\n", ret);
                goto err_rx_attach;
+       }
 
        /*
         * Prefetch enough data to satisfy target
@@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
                8 + /* llc snap */
                2; /* ip4 dscp or ip6 priority */
 
-       return htt;
+       return 0;
 
 err_rx_attach:
        ath10k_htt_tx_detach(htt);
 err_htc_attach:
-       kfree(htt);
-       return NULL;
+       return ret;
 }
 
 #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
@@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt)
 {
        ath10k_htt_rx_detach(htt);
        ath10k_htt_tx_detach(htt);
-       kfree(htt);
 }
index a7a7aa0..318be46 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <linux/bug.h>
 
-#include "core.h"
 #include "htc.h"
 #include "rx_desc.h"
 
@@ -1317,7 +1316,7 @@ struct htt_rx_desc {
 #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
 #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
 
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach(struct ath10k *ar);
 int ath10k_htt_attach_target(struct ath10k_htt *htt);
 void ath10k_htt_detach(struct ath10k_htt *htt);
 
index de058d7..e784c40 100644 (file)
@@ -15,6 +15,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "core.h"
 #include "htc.h"
 #include "htt.h"
 #include "txrx.h"
@@ -803,6 +804,37 @@ static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
        return false;
 }
 
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags, info;
+       bool is_ip4, is_ip6;
+       bool is_tcp, is_udp;
+       bool ip_csum_ok, tcpudp_csum_ok;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+       info = __le32_to_cpu(rxd->msdu_start.info1);
+
+       is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO);
+       is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO);
+       is_tcp = !!(info & RX_MSDU_START_INFO1_TCP_PROTO);
+       is_udp = !!(info & RX_MSDU_START_INFO1_UDP_PROTO);
+       ip_csum_ok = !(flags & RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL);
+       tcpudp_csum_ok = !(flags & RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL);
+
+       if (!is_ip4 && !is_ip6)
+               return CHECKSUM_NONE;
+       if (!is_tcp && !is_udp)
+               return CHECKSUM_NONE;
+       if (!ip_csum_ok)
+               return CHECKSUM_NONE;
+       if (!tcpudp_csum_ok)
+               return CHECKSUM_NONE;
+
+       return CHECKSUM_UNNECESSARY;
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                  struct htt_rx_indication *rx)
 {
@@ -814,6 +846,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
        u8 *fw_desc;
        int i, j;
        int ret;
+       int ip_summed;
 
        memset(&info, 0, sizeof(info));
 
@@ -888,6 +921,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                continue;
                        }
 
+                       /* The skb is not yet processed and it may be
+                        * reallocated. Since the offload is in the original
+                        * skb extract the checksum now and assign it later */
+                       ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
+
                        info.skb     = msdu_head;
                        info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
                        info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
@@ -913,6 +951,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
                                ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
 
+                       info.skb->ip_summed = ip_summed;
+
                        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
                                        info.skb->data, info.skb->len);
                        ath10k_process_rx(htt->ar, &info);
@@ -979,6 +1019,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
        info.status = HTT_RX_IND_MPDU_STATUS_OK;
        info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
                                RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb);
 
        if (tkip_mic_err) {
                ath10k_warn("tkip mic error\n");
@@ -1036,7 +1077,7 @@ end:
 
 void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 {
-       struct ath10k_htt *htt = ar->htt;
+       struct ath10k_htt *htt = &ar->htt;
        struct htt_resp *resp = (struct htt_resp *)skb->data;
 
        /* confirm alignment */
index ef79106..656c254 100644 (file)
@@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
 
        /* At the beginning free queue number should hint us the maximum
         * queue length */
-       pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+       pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id;
        htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
                                                                   pipe);
 
@@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
-       struct ath10k_htt *htt = ar->htt;
+       struct ath10k_htt *htt = &ar->htt;
 
        if (skb_cb->htt.is_conf) {
                dev_kfree_skb_any(skb);
@@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
 
        ATH10K_SKB_CB(skb)->htt.is_conf = true;
 
-       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
                dev_kfree_skb_any(skb);
                return ret;
@@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
 
        ATH10K_SKB_CB(skb)->htt.is_conf = true;
 
-       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
        if (ret) {
                dev_kfree_skb_any(skb);
                return ret;
@@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        skb_cb->htt.refcount = 2;
        skb_cb->htt.msdu = msdu;
 
-       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
                goto err;
 
@@ -465,6 +465,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        flags1  = 0;
        flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
        flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+       flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
+       flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
 
        frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
 
@@ -486,7 +488,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
        skb_cb->htt.txfrag = txfrag;
        skb_cb->htt.msdu = msdu;
 
-       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
        if (res)
                goto err;
 
index da5c333..cf2ba4d 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                .macaddr = macaddr,
        };
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
                arg.key_flags = WMI_KEY_PAIRWISE;
        else
@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
        struct ath10k *ar = arvif->ar;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        INIT_COMPLETION(ar->install_key_done);
 
        ret = ath10k_send_key(arvif, key, cmd, macaddr);
@@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        return 0;
 }
 
+static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
+                             ATH10K_RTS_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                        value);
+}
+
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
+                               ATH10K_FRAGMT_THRESHOLD_MIN,
+                               ATH10K_FRAGMT_THRESHOLD_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                        value);
+}
+
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
        int ret;
@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
        spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+       struct ath10k_peer *peer, *tmp;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = wait_for_completion_timeout(&ar->vdev_setup_done,
                                          ATH10K_VDEV_SETUP_TIMEOUT_HZ);
        if (ret == 0)
@@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
                return;
@@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
@@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        enum wmi_sta_ps_mode psmode;
        int ret;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
@@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
                                      struct ieee80211_bss_conf *bss_conf,
                                      struct wmi_peer_assoc_complete_arg *arg)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
        memcpy(arg->addr, sta->addr, ETH_ALEN);
        arg->vdev_id = arvif->vdev_id;
        arg->peer_aid = sta->aid;
@@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
        const u8 *rsnie = NULL;
        const u8 *wpaie = NULL;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
                               info->bssid, NULL, 0, 0, 0);
        if (bss) {
@@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
        u32 ratemask;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
        ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
        rates = sband->bitrates;
@@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        int smps;
        int i, n;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (!ht_cap->ht_supported)
                return;
 
@@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
        u32 uapsd = 0;
        u32 max_sp = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (sta->wme)
                arg->peer_flags |= WMI_PEER_QOS;
 
@@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
 {
        struct wmi_peer_assoc_complete_arg arg;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
 
        ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
@@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        struct ieee80211_sta *ap_sta;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        rcu_read_lock();
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        /*
         * For some reason, calling VDEV-DOWN before VDEV-STOP
         * makes the FW to send frames via HTT after disassociation.
@@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
        if (ret) {
                ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
@@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
                ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
@@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        int ret;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bands = hw->wiphy->bands;
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (!bands[band])
@@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-                               struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct reg_dmn_pair_mapping *regpair;
-       struct ath10k *ar = hw->priv;
        int ret;
 
-       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+       lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
                ath10k_warn("could not update channel list (%d)\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 }
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ath10k *ar = hw->priv;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON)
+               ath10k_regd_update(ar);
+       mutex_unlock(&ar->conf_mutex);
+}
+
 /***************/
 /* TX handlers */
 /***************/
@@ -1322,9 +1406,9 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
                return;
 
        qos_ctl = ieee80211_get_qos_ctl(hdr);
-       memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
-               skb->len - ieee80211_hdrlen(hdr->frame_control));
-       skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+       memmove(skb->data + IEEE80211_QOS_CTL_LEN,
+               skb->data, (void *)qos_ctl - (void *)skb->data);
+       skb_pull(skb, IEEE80211_QOS_CTL_LEN);
 }
 
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
@@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
        int ret;
 
        if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else if (ieee80211_is_nullfunc(hdr->frame_control))
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
                 * those frames when it detects link/beacon loss and depends on
                 * the tx status to be correct. */
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else
-               ret = ath10k_htt_tx(ar->htt, skb);
+               ret = ath10k_htt_tx(&ar->htt, skb);
 
        if (ret) {
                ath10k_warn("tx failed (%d). dropping packet.\n", ret);
@@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
                ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               spin_unlock_bh(&ar->data_lock);
                return -EIO;
        }
 
@@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
                tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
        }
 
-       ath10k_tx_h_qos_workaround(hw, control, skb);
-       ath10k_tx_h_update_wep_key(skb);
-       ath10k_tx_h_add_p2p_noa_ie(ar, skb);
-       ath10k_tx_h_seq_no(skb);
+       /* it makes no sense to process injected frames like that */
+       if (info->control.vif &&
+           info->control.vif->type != NL80211_IFTYPE_MONITOR) {
+               ath10k_tx_h_qos_workaround(hw, control, skb);
+               ath10k_tx_h_update_wep_key(skb);
+               ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+               ath10k_tx_h_seq_no(skb);
+       }
 
        memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
        ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
@@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
+void ath10k_halt(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+       ath10k_offchan_tx_purge(ar);
+       ath10k_peer_cleanup_all(ar);
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               del_timer(&ar->scan.timeout);
+               ar->scan.in_progress = false;
+               ieee80211_scan_completed(ar->hw, true);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
-       int ret;
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_OFF &&
+           ar->state != ATH10K_STATE_RESTARTING) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not init hif (%d)\n", ret);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       if (ar->state == ATH10K_STATE_OFF)
+               ar->state = ATH10K_STATE_ON;
+       else if (ar->state == ATH10K_STATE_RESTARTING)
+               ar->state = ATH10K_STATE_RESTARTED;
 
        ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
        if (ret)
@@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
+       ath10k_regd_update(ar);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
        return 0;
 }
 
@@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
-       /* avoid leaks in case FW never confirms scan for offchannel */
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON ||
+           ar->state == ATH10K_STATE_RESTARTED ||
+           ar->state == ATH10K_STATE_WEDGED)
+               ath10k_halt(ar);
+
+       ar->state = ATH10K_STATE_OFF;
+       mutex_unlock(&ar->conf_mutex);
+
        cancel_work_sync(&ar->offchan_tx_work);
-       ath10k_offchan_tx_purge(ar);
+       cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
        struct ath10k_generic_iter ar_iter;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       ieee80211_iterate_active_interfaces_atomic(
+               ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_ps_iter, &ar_iter);
+
+       if (ar_iter.ret)
+               ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
        struct ath10k *ar = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
        int ret = 0;
-       u32 flags;
 
        mutex_lock(&ar->conf_mutex);
 
@@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                spin_unlock_bh(&ar->data_lock);
        }
 
-       if (changed & IEEE80211_CONF_CHANGE_PS) {
-               memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-               ar_iter.ar = ar;
-               flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-               ieee80211_iterate_active_interfaces_atomic(hw,
-                                                          flags,
-                                                          ath10k_ps_iter,
-                                                          &ar_iter);
-
-               ret = ar_iter.ret;
-       }
+       if (changed & IEEE80211_CONF_CHANGE_PS)
+               ath10k_config_ps(ar);
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
                if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                        ret = ath10k_monitor_destroy(ar);
        }
 
+       ath10k_wmi_flush_tx(ar);
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -1761,6 +1925,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
+       memset(arvif, 0, sizeof(*arvif));
+
        arvif->ar = ar;
        arvif->vif = vif;
 
@@ -1859,6 +2025,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
        }
 
+       ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
+       if (ret)
+               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
+       ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
+       if (ret)
+               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
@@ -2164,6 +2340,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
                        arg.ssids[i].len  = req->ssids[i].ssid_len;
                        arg.ssids[i].ssid = req->ssids[i].ssid;
                }
+       } else {
+               arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
        }
 
        if (req->n_channels) {
@@ -2363,6 +2541,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
        u32 value = 0;
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
                return 0;
 
@@ -2558,11 +2738,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
 
-       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
-                                                rts);
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
        if (ar_iter->ret)
                ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
                            arvif->vdev_id);
@@ -2581,8 +2766,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_rts_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_rts_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2593,17 +2779,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_generic_iter *ar_iter = data;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-       int ret;
 
-       frag = clamp_t(u32, frag,
-                      ATH10K_FRAGMT_THRESHOLD_MIN,
-                      ATH10K_FRAGMT_THRESHOLD_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-                                       frag);
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
 
-       ar_iter->ret = ret;
+       ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
        if (ar_iter->ret)
                ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
                            arvif->vdev_id);
@@ -2622,8 +2808,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_frag_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_frag_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2632,6 +2819,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath10k *ar = hw->priv;
+       bool skip;
        int ret;
 
        /* mac80211 doesn't care if we really xmit queued frames or not
@@ -2639,16 +2827,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
        if (drop)
                return;
 
-       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state == ATH10K_STATE_WEDGED)
+               goto skip;
+
+       ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
                        bool empty;
-                       spin_lock_bh(&ar->htt->tx_lock);
-                       empty = bitmap_empty(ar->htt->used_msdu_ids,
-                                            ar->htt->max_num_pending_tx);
-                       spin_unlock_bh(&ar->htt->tx_lock);
-                       (empty);
+
+                       spin_lock_bh(&ar->htt.tx_lock);
+                       empty = bitmap_empty(ar->htt.used_msdu_ids,
+                                            ar->htt.max_num_pending_tx);
+                       spin_unlock_bh(&ar->htt.tx_lock);
+
+                       skip = (ar->state == ATH10K_STATE_WEDGED);
+
+                       (empty || skip);
                }), ATH10K_FLUSH_TIMEOUT_HZ);
-       if (ret <= 0)
+
+       if (ret <= 0 || skip)
                ath10k_warn("tx not flushed\n");
+
+skip:
+       mutex_unlock(&ar->conf_mutex);
 }
 
 /* TODO: Implement this function properly
@@ -2660,6 +2861,118 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
        return 1;
 }
 
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+                         struct cfg80211_wowlan *wowlan)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ar->is_target_paused = false;
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret) {
+               ath10k_warn("could not suspend target (%d)\n", ret);
+               return 1;
+       }
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                              ar->is_target_paused == true,
+                                              1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               goto resume;
+       }
+
+       ret = ath10k_hif_suspend(ar);
+       if (ret) {
+               ath10k_warn("could not suspend hif (%d)\n", ret);
+               goto resume;
+       }
+
+       return 0;
+resume:
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+       return 1;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_hif_resume(ar);
+       if (ret) {
+               ath10k_warn("could not resume hif (%d)\n", ret);
+               return 1;
+       }
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret) {
+               ath10k_warn("could not resume target (%d)\n", ret);
+               return 1;
+       }
+
+       return 0;
+}
+#endif
+
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+
+       /* If device failed to restart it will be in a different state, e.g.
+        * ATH10K_STATE_WEDGED */
+       if (ar->state == ATH10K_STATE_RESTARTED) {
+               ath10k_info("device successfully recovered\n");
+               ar->state = ATH10K_STATE_ON;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
+                            struct survey_info *survey)
+{
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_supported_band *sband;
+       struct survey_info *ar_survey = &ar->survey[idx];
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+       if (sband && idx >= sband->n_channels) {
+               idx -= sband->n_channels;
+               sband = NULL;
+       }
+
+       if (!sband)
+               sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+       if (!sband || idx >= sband->n_channels) {
+               ret = -ENOENT;
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       memcpy(survey, ar_survey, sizeof(*survey));
+       spin_unlock_bh(&ar->data_lock);
+
+       survey->channel = &sband->channels[idx];
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -2680,6 +2993,12 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+       .restart_complete               = ath10k_restart_complete,
+       .get_survey                     = ath10k_get_survey,
+#ifdef CONFIG_PM
+       .suspend                        = ath10k_suspend,
+       .resume                         = ath10k_resume,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -2797,9 +3116,15 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        .max    = 8,
        .types  = BIT(NL80211_IFTYPE_STATION)
                | BIT(NL80211_IFTYPE_P2P_CLIENT)
-               | BIT(NL80211_IFTYPE_P2P_GO)
-               | BIT(NL80211_IFTYPE_AP)
-       }
+       },
+       {
+       .max    = 3,
+       .types  = BIT(NL80211_IFTYPE_P2P_GO)
+       },
+       {
+       .max    = 7,
+       .types  = BIT(NL80211_IFTYPE_AP)
+       },
 };
 
 static const struct ieee80211_iface_combination ath10k_if_comb = {
@@ -2814,19 +3139,18 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
 {
        struct ieee80211_sta_vht_cap vht_cap = {0};
        u16 mcs_map;
+       int i;
 
        vht_cap.vht_supported = 1;
        vht_cap.cap = ar->vht_cap_info;
 
-       /* FIXME: check dynamically how many streams board supports */
-       mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
-               IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
-               IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+       mcs_map = 0;
+       for (i = 0; i < 8; i++) {
+               if (i < ar->num_rf_chains)
+                       mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2);
+               else
+                       mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2);
+       }
 
        vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
        vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
@@ -2889,7 +3213,7 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
        if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
                ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
-       for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+       for (i = 0; i < ar->num_rf_chains; i++)
                ht_cap.mcs.rx_mask[i] = 0xFF;
 
        ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
@@ -2948,8 +3272,10 @@ int ath10k_mac_register(struct ath10k *ar)
                channels = kmemdup(ath10k_2ghz_channels,
                                   sizeof(ath10k_2ghz_channels),
                                   GFP_KERNEL);
-               if (!channels)
-                       return -ENOMEM;
+               if (!channels) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
 
                band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
                band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
@@ -2968,11 +3294,8 @@ int ath10k_mac_register(struct ath10k *ar)
                                   sizeof(ath10k_5ghz_channels),
                                   GFP_KERNEL);
                if (!channels) {
-                       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
-                               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
-                               kfree(band->channels);
-                       }
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_free;
                }
 
                band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
@@ -3032,29 +3355,36 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
        ar->hw->wiphy->n_iface_combinations = 1;
 
+       ar->hw->netdev_features = NETIF_F_HW_CSUM;
+
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
                ath10k_err("Regulatory initialization failed\n");
-               return ret;
+               goto err_free;
        }
 
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
                ath10k_err("ieee80211 registration failed: %d\n", ret);
-               return ret;
+               goto err_free;
        }
 
        if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
                ret = regulatory_hint(ar->hw->wiphy,
                                      ar->ath_common.regulatory.alpha2);
                if (ret)
-                       goto exit;
+                       goto err_unregister;
        }
 
        return 0;
-exit:
+
+err_unregister:
        ieee80211_unregister_hw(ar->hw);
+err_free:
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
        return ret;
 }
 
index 27fc92e..6fce9bf 100644 (file)
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
 void ath10k_reset_scan(unsigned long ptr);
 void ath10k_offchan_tx_purge(struct ath10k *ar);
 void ath10k_offchan_tx_work(struct work_struct *work);
+void ath10k_halt(struct ath10k *ar);
 
 static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
 {
index 33af467..e2f9ef5 100644 (file)
@@ -32,7 +32,7 @@
 #include "ce.h"
 #include "pci.h"
 
-unsigned int ath10k_target_ps;
+static unsigned int ath10k_target_ps;
 module_param(ath10k_target_ps, uint, 0644);
 MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
 
@@ -54,6 +54,10 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
                                             int num);
 static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
+static void ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_reset_target(struct ath10k *ar);
+static int ath10k_pci_start_intr(struct ath10k *ar);
+static void ath10k_pci_stop_intr(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
        /* host->target HTC control and raw streams */
@@ -718,6 +722,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
                           reg_dump_values[i + 1],
                           reg_dump_values[i + 2],
                           reg_dump_values[i + 3]);
+
+       ieee80211_queue_work(ar->hw, &ar->restart_work);
 }
 
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
@@ -744,8 +750,8 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
        ath10k_ce_per_engine_service(ar, pipe);
 }
 
-static void ath10k_pci_hif_post_init(struct ath10k *ar,
-                                    struct ath10k_hif_cb *callbacks)
+static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
+                                        struct ath10k_hif_cb *callbacks)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
@@ -1250,10 +1256,25 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar)
        }
 }
 
+static void ath10k_pci_disable_irqs(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int i;
+
+       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+               disable_irq(ar_pci->pdev->irq + i);
+}
+
 static void ath10k_pci_hif_stop(struct ath10k *ar)
 {
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
        ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
 
+       /* Irqs are never explicitly re-enabled. They are implicitly re-enabled
+        * by ath10k_pci_start_intr(). */
+       ath10k_pci_disable_irqs(ar);
+
        ath10k_pci_stop_ce(ar);
 
        /* At this point, asynchronous threads are stopped, the target should
@@ -1263,7 +1284,8 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
        ath10k_pci_process_ce(ar);
        ath10k_pci_cleanup_ce(ar);
        ath10k_pci_buffer_cleanup(ar);
-       ath10k_pci_ce_deinit(ar);
+
+       ar_pci->started = 0;
 }
 
 static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
@@ -1735,6 +1757,124 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = ath10k_pci_start_intr(ar);
+       if (ret) {
+               ath10k_err("could not start interrupt handling (%d)\n", ret);
+               goto err;
+       }
+
+       /*
+        * Bring the target up cleanly.
+        *
+        * The target may be in an undefined state with an AUX-powered Target
+        * and a Host in WoW mode. If the Host crashes, loses power, or is
+        * restarted (without unloading the driver) then the Target is left
+        * (aux) powered and running. On a subsequent driver load, the Target
+        * is in an unexpected state. We try to catch that here in order to
+        * reset the Target and retry the probe.
+        */
+       ath10k_pci_device_reset(ar);
+
+       ret = ath10k_pci_reset_target(ar);
+       if (ret)
+               goto err_irq;
+
+       if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+               /* Force AWAKE forever */
+               ath10k_do_pci_wake(ar);
+
+       ret = ath10k_pci_ce_init(ar);
+       if (ret)
+               goto err_ps;
+
+       ret = ath10k_pci_init_config(ar);
+       if (ret)
+               goto err_ce;
+
+       ret = ath10k_pci_wake_target_cpu(ar);
+       if (ret) {
+               ath10k_err("could not wake up target CPU (%d)\n", ret);
+               goto err_ce;
+       }
+
+       return 0;
+
+err_ce:
+       ath10k_pci_ce_deinit(ar);
+err_ps:
+       if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+               ath10k_do_pci_sleep(ar);
+err_irq:
+       ath10k_pci_stop_intr(ar);
+err:
+       return ret;
+}
+
+static void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_pci_stop_intr(ar);
+
+       ath10k_pci_ce_deinit(ar);
+       if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+               ath10k_do_pci_sleep(ar);
+}
+
+#ifdef CONFIG_PM
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_hif_suspend(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct pci_dev *pdev = ar_pci->pdev;
+       u32 val;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0x3) {
+               pci_save_state(pdev);
+               pci_disable_device(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      (val & 0xffffff00) | 0x03);
+       }
+
+       return 0;
+}
+
+static int ath10k_pci_hif_resume(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct pci_dev *pdev = ar_pci->pdev;
+       u32 val;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0) {
+               pci_restore_state(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      val & 0xffffff00);
+               /*
+                * Suspend/Resume resets the PCI configuration space,
+                * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+                * to keep PCI Tx retries from interfering with C3 CPU state
+                */
+               pci_read_config_dword(pdev, 0x40, &val);
+
+               if ((val & 0x0000ff00) != 0)
+                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+       }
+
+       return 0;
+}
+#endif
+
 static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .send_head              = ath10k_pci_hif_send_head,
        .exchange_bmi_msg       = ath10k_pci_hif_exchange_bmi_msg,
@@ -1743,8 +1883,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
        .map_service_to_pipe    = ath10k_pci_hif_map_service_to_pipe,
        .get_default_pipe       = ath10k_pci_hif_get_default_pipe,
        .send_complete_check    = ath10k_pci_hif_send_complete_check,
-       .init                   = ath10k_pci_hif_post_init,
+       .set_callbacks          = ath10k_pci_hif_set_callbacks,
        .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
+       .power_up               = ath10k_pci_hif_power_up,
+       .power_down             = ath10k_pci_hif_power_down,
+#ifdef CONFIG_PM
+       .suspend                = ath10k_pci_hif_suspend,
+       .resume                 = ath10k_pci_hif_resume,
+#endif
 };
 
 static void ath10k_pci_ce_tasklet(unsigned long ptr)
@@ -1872,8 +2018,13 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
        ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
                          ath10k_pci_msi_fw_handler,
                          IRQF_SHARED, "ath10k_pci", ar);
-       if (ret)
+       if (ret) {
+               ath10k_warn("request_irq(%d) failed %d\n",
+                           ar_pci->pdev->irq + MSI_ASSIGN_FW, ret);
+
+               pci_disable_msi(ar_pci->pdev);
                return ret;
+       }
 
        for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
                ret = request_irq(ar_pci->pdev->irq + i,
@@ -2059,9 +2210,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar)
        return 0;
 }
 
-static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+static void ath10k_pci_device_reset(struct ath10k *ar)
 {
-       struct ath10k *ar = ar_pci->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *mem = ar_pci->mem;
        int i;
        u32 val;
@@ -2118,9 +2269,12 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
                case ATH10K_PCI_FEATURE_MSI_X:
                        ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
                        break;
-               case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+               case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND:
                        ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
                        break;
+               case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
+                       ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n");
+                       break;
                }
        }
 }
@@ -2145,7 +2299,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        switch (pci_dev->device) {
        case QCA988X_1_0_DEVICE_ID:
-               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features);
                break;
        case QCA988X_2_0_DEVICE_ID:
                set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
@@ -2156,10 +2310,12 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_ar_pci;
        }
 
+       if (ath10k_target_ps)
+               set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features);
+
        ath10k_pci_dump_features(ar_pci);
 
-       ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
-                               &ath10k_pci_hif_ops);
+       ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
        if (!ar) {
                ath10k_err("ath10k_core_create failed!\n");
                ret = -EINVAL;
@@ -2167,7 +2323,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        }
 
        /* Enable QCA988X_1.0 HW workarounds */
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features))
                spin_lock_init(&ar_pci->hw_v1_workaround_lock);
 
        ar_pci->ar = ar;
@@ -2241,62 +2397,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        ar_pci->cacheline_sz = dma_get_cache_alignment();
 
-       ret = ath10k_pci_start_intr(ar);
-       if (ret) {
-               ath10k_err("could not start interrupt handling (%d)\n", ret);
-               goto err_iomap;
-       }
-
-       /*
-        * Bring the target up cleanly.
-        *
-        * The target may be in an undefined state with an AUX-powered Target
-        * and a Host in WoW mode. If the Host crashes, loses power, or is
-        * restarted (without unloading the driver) then the Target is left
-        * (aux) powered and running. On a subsequent driver load, the Target
-        * is in an unexpected state. We try to catch that here in order to
-        * reset the Target and retry the probe.
-        */
-       ath10k_pci_device_reset(ar_pci);
-
-       ret = ath10k_pci_reset_target(ar);
-       if (ret)
-               goto err_intr;
-
-       if (ath10k_target_ps) {
-               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
-       } else {
-               /* Force AWAKE forever */
-               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
-               ath10k_do_pci_wake(ar);
-       }
-
-       ret = ath10k_pci_ce_init(ar);
-       if (ret)
-               goto err_intr;
-
-       ret = ath10k_pci_init_config(ar);
-       if (ret)
-               goto err_ce;
-
-       ret = ath10k_pci_wake_target_cpu(ar);
-       if (ret) {
-               ath10k_err("could not wake up target CPU (%d)\n", ret);
-               goto err_ce;
-       }
-
        ret = ath10k_core_register(ar);
        if (ret) {
                ath10k_err("could not register driver core (%d)\n", ret);
-               goto err_ce;
+               goto err_iomap;
        }
 
        return 0;
 
-err_ce:
-       ath10k_pci_ce_deinit(ar);
-err_intr:
-       ath10k_pci_stop_intr(ar);
 err_iomap:
        pci_iounmap(pdev, mem);
 err_master:
@@ -2333,7 +2441,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        tasklet_kill(&ar_pci->msi_fw_err);
 
        ath10k_core_unregister(ar);
-       ath10k_pci_stop_intr(ar);
 
        pci_set_drvdata(pdev, NULL);
        pci_iounmap(pdev, ar_pci->mem);
@@ -2345,128 +2452,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        kfree(ar_pci);
 }
 
-#if defined(CONFIG_PM_SLEEP)
-
-#define ATH10K_PCI_PM_CONTROL 0x44
-
-static int ath10k_pci_suspend(struct device *device)
-{
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct ath10k *ar = pci_get_drvdata(pdev);
-       struct ath10k_pci *ar_pci;
-       u32 val;
-       int ret, retval;
-
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-       if (!ar)
-               return -ENODEV;
-
-       ar_pci = ath10k_pci_priv(ar);
-       if (!ar_pci)
-               return -ENODEV;
-
-       if (ath10k_core_target_suspend(ar))
-               return -EBUSY;
-
-       ret = wait_event_interruptible_timeout(ar->event_queue,
-                                               ar->is_target_paused == true,
-                                               1 * HZ);
-       if (ret < 0) {
-               ath10k_warn("suspend interrupted (%d)\n", ret);
-               retval = ret;
-               goto resume;
-       } else if (ret == 0) {
-               ath10k_warn("suspend timed out - target pause event never came\n");
-               retval = EIO;
-               goto resume;
-       }
-
-       /*
-        * reset is_target_paused and host can check that in next time,
-        * or it will always be TRUE and host just skip the waiting
-        * condition, it causes target assert due to host already
-        * suspend
-        */
-       ar->is_target_paused = false;
-
-       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-       if ((val & 0x000000ff) != 0x3) {
-               pci_save_state(pdev);
-               pci_disable_device(pdev);
-               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-                                      (val & 0xffffff00) | 0x03);
-       }
-
-       return 0;
-resume:
-       ret = ath10k_core_target_resume(ar);
-       if (ret)
-               ath10k_warn("could not resume (%d)\n", ret);
-
-       return retval;
-}
-
-static int ath10k_pci_resume(struct device *device)
-{
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct ath10k *ar = pci_get_drvdata(pdev);
-       struct ath10k_pci *ar_pci;
-       int ret;
-       u32 val;
-
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
-       if (!ar)
-               return -ENODEV;
-       ar_pci = ath10k_pci_priv(ar);
-
-       if (!ar_pci)
-               return -ENODEV;
-
-       ret = pci_enable_device(pdev);
-       if (ret) {
-               ath10k_warn("cannot enable PCI device: %d\n", ret);
-               return ret;
-       }
-
-       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
-       if ((val & 0x000000ff) != 0) {
-               pci_restore_state(pdev);
-               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
-                                      val & 0xffffff00);
-               /*
-                * Suspend/Resume resets the PCI configuration space,
-                * so we have to re-disable the RETRY_TIMEOUT register (0x41)
-                * to keep PCI Tx retries from interfering with C3 CPU state
-                */
-               pci_read_config_dword(pdev, 0x40, &val);
-
-               if ((val & 0x0000ff00) != 0)
-                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-       }
-
-       ret = ath10k_core_target_resume(ar);
-       if (ret)
-               ath10k_warn("target resume failed: %d\n", ret);
-
-       return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
-                        ath10k_pci_suspend,
-                        ath10k_pci_resume);
-
-#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
-
-#else
-
-#define ATH10K_PCI_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
 MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
 
 static struct pci_driver ath10k_pci_driver = {
@@ -2474,7 +2459,6 @@ static struct pci_driver ath10k_pci_driver = {
        .id_table = ath10k_pci_id_table,
        .probe = ath10k_pci_probe,
        .remove = ath10k_pci_remove,
-       .driver.pm = ATH10K_PCI_PM_OPS,
 };
 
 static int __init ath10k_pci_init(void)
index d2a055a..871bb33 100644 (file)
@@ -152,7 +152,8 @@ struct service_to_pipe {
 
 enum ath10k_pci_features {
        ATH10K_PCI_FEATURE_MSI_X                = 0,
-       ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND    = 1,
+       ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND    = 1,
+       ATH10K_PCI_FEATURE_SOC_POWER_SAVE       = 2,
 
        /* keep last */
        ATH10K_PCI_FEATURE_COUNT
@@ -311,7 +312,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        void __iomem *addr = ar_pci->mem;
 
-       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
                unsigned long irq_flags;
 
                spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
@@ -335,20 +336,22 @@ static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
        return ioread32(ar_pci->mem + offset);
 }
 
-extern unsigned int ath10k_target_ps;
-
 void ath10k_do_pci_wake(struct ath10k *ar);
 void ath10k_do_pci_sleep(struct ath10k *ar);
 
 static inline void ath10k_pci_wake(struct ath10k *ar)
 {
-       if (ath10k_target_ps)
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
                ath10k_do_pci_wake(ar);
 }
 
 static inline void ath10k_pci_sleep(struct ath10k *ar)
 {
-       if (ath10k_target_ps)
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
                ath10k_do_pci_sleep(ar);
 }
 
index 7d4b798..55f90c7 100644 (file)
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->state == ATH10K_STATE_WEDGED) {
+               ath10k_warn("wmi flush skipped - device is wedged anyway\n");
+               return;
+       }
+
        ret = wait_event_timeout(ar->wmi.wq,
                                 atomic_read(&ar->wmi.pending_tx_count) == 0,
                                 5*HZ);
@@ -111,7 +118,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
 
        trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
 
-       status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+       status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
        if (status) {
                dev_kfree_skb_any(skb);
                atomic_dec(&ar->wmi.pending_tx_count);
@@ -383,9 +390,82 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        return 0;
 }
 
+static int freq_to_idx(struct ath10k *ar, int freq)
+{
+       struct ieee80211_supported_band *sband;
+       int band, ch, idx = 0;
+
+       for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+               sband = ar->hw->wiphy->bands[band];
+               if (!sband)
+                       continue;
+
+               for (ch = 0; ch < sband->n_channels; ch++, idx++)
+                       if (sband->channels[ch].center_freq == freq)
+                               goto exit;
+       }
+
+exit:
+       return idx;
+}
+
 static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
 {
-       ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+       struct wmi_chan_info_event *ev;
+       struct survey_info *survey;
+       u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
+       int idx;
+
+       ev = (struct wmi_chan_info_event *)skb->data;
+
+       err_code = __le32_to_cpu(ev->err_code);
+       freq = __le32_to_cpu(ev->freq);
+       cmd_flags = __le32_to_cpu(ev->cmd_flags);
+       noise_floor = __le32_to_cpu(ev->noise_floor);
+       rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
+       cycle_count = __le32_to_cpu(ev->cycle_count);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+                  err_code, freq, cmd_flags, noise_floor, rx_clear_count,
+                  cycle_count);
+
+       spin_lock_bh(&ar->data_lock);
+
+       if (!ar->scan.in_progress) {
+               ath10k_warn("chan info event without a scan request?\n");
+               goto exit;
+       }
+
+       idx = freq_to_idx(ar, freq);
+       if (idx >= ARRAY_SIZE(ar->survey)) {
+               ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n",
+                           freq, idx);
+               goto exit;
+       }
+
+       if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+               /* During scanning chan info is reported twice for each
+                * visited channel. The reported cycle count is global
+                * and per-channel cycle count must be calculated */
+
+               cycle_count -= ar->survey_last_cycle_count;
+               rx_clear_count -= ar->survey_last_rx_clear_count;
+
+               survey = &ar->survey[idx];
+               survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
+               survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+               survey->noise = noise_floor;
+               survey->filled = SURVEY_INFO_CHANNEL_TIME |
+                                SURVEY_INFO_CHANNEL_TIME_RX |
+                                SURVEY_INFO_NOISE_DBM;
+       }
+
+       ar->survey_last_rx_clear_count = rx_clear_count;
+       ar->survey_last_cycle_count = cycle_count;
+
+exit:
+       spin_unlock_bh(&ar->data_lock);
 }
 
 static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
@@ -501,8 +581,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
        ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
                                    (u8 *)skb_tail_pointer(bcn) - ies);
        if (!ie) {
-               /* highly unlikely for mac80211 */
-               ath10k_warn("no tim ie found;\n");
+               if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+                       ath10k_warn("no tim ie found;\n");
                return;
        }
 
@@ -861,6 +941,13 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
                (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
        ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
        ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+       ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+
+       if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+               ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+                           ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+               ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+       }
 
        ar->ath_common.regulatory.current_rd =
                __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
@@ -885,7 +972,7 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
        }
 
        ath10k_dbg(ATH10K_DBG_WMI,
-                  "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n",
+                  "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
                   __le32_to_cpu(ev->sw_version),
                   __le32_to_cpu(ev->sw_version_1),
                   __le32_to_cpu(ev->abi_version),
@@ -894,7 +981,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
                   __le32_to_cpu(ev->vht_cap_info),
                   __le32_to_cpu(ev->vht_supp_mcs),
                   __le32_to_cpu(ev->sys_cap_info),
-                  __le32_to_cpu(ev->num_mem_reqs));
+                  __le32_to_cpu(ev->num_mem_reqs),
+                  __le32_to_cpu(ev->num_rf_chains));
 
        complete(&ar->wmi.service_ready);
 }
@@ -1114,7 +1202,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
        /* connect to control service */
        conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
 
-       status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+       status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
        if (status) {
                ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
                            status);
@@ -1748,6 +1836,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
        if (arg->key_data)
                memcpy(cmd->key_data, arg->key_data, arg->key_len);
 
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev install key idx %d cipher %d len %d\n",
+                  arg->key_idx, arg->key_cipher, arg->key_len);
        return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
 }
 
@@ -2011,6 +2102,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
        cmd->peer_vht_rates.tx_mcs_set =
                __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
 
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer assoc vdev %d addr %pM\n",
+                  arg->vdev_id, arg->addr);
        return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
 }
 
@@ -2079,3 +2173,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
        ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
        return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
 }
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+                            enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+       struct wmi_force_fw_hang_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+       cmd->type = __cpu_to_le32(type);
+       cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+                  type, delay_ms);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
index 9555f5a..2c5a4f8 100644 (file)
@@ -416,6 +416,7 @@ enum wmi_cmd_id {
        WMI_PDEV_FTM_INTG_CMDID,
        WMI_VDEV_SET_KEEPALIVE_CMDID,
        WMI_VDEV_GET_KEEPALIVE_CMDID,
+       WMI_FORCE_FW_HANG_CMDID,
 
        /* GPIO Configuration */
        WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
@@ -2930,6 +2931,11 @@ struct wmi_chan_info_event {
        __le32 cycle_count;
 } __packed;
 
+#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
+
+/* FIXME: empirically extrapolated */
+#define WMI_CHAN_INFO_MSEC(x) ((x) / 76595)
+
 /* Beacon filter wmi command info */
 #define BCN_FLT_MAX_SUPPORTED_IES      256
 #define BCN_FLT_MAX_ELEMS_IE_LIST      (BCN_FLT_MAX_SUPPORTED_IES / 32)
@@ -2972,6 +2978,22 @@ struct wmi_sta_keepalive_cmd {
        struct wmi_sta_keepalive_arp_resp arp_resp;
 } __packed;
 
+enum wmi_force_fw_hang_type {
+       WMI_FORCE_FW_HANG_ASSERT = 1,
+       WMI_FORCE_FW_HANG_NO_DETECT,
+       WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+       WMI_FORCE_FW_HANG_EMPTY_POINT,
+       WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+       WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+       __le32 type;
+       __le32 delay_ms;
+} __packed;
+
 #define ATH10K_RTS_MAX         2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN    540
 #define ATH10K_FRAGMT_THRESHOLD_MAX    2346
@@ -3048,5 +3070,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
                        const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+                            enum wmi_force_fw_hang_type type, u32 delay_ms);
 
 #endif /* _WMI_H_ */
index 2d691b8..74bd54d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/average.h>
 #include <linux/leds.h>
 #include <net/mac80211.h>
+#include <net/cfg80211.h>
 
 /* RX/TX descriptor hw structs
  * TODO: Driver part should only see sw structs */
index ce67ab7..48161ed 100644 (file)
@@ -56,6 +56,7 @@
 #include <linux/etherdevice.h>
 #include <linux/nl80211.h>
 
+#include <net/cfg80211.h>
 #include <net/ieee80211_radiotap.h>
 
 #include <asm/unaligned.h>
@@ -165,28 +166,36 @@ static const struct ieee80211_rate ath5k_rates[] = {
          .flags = IEEE80211_RATE_SHORT_PREAMBLE },
        { .bitrate = 60,
          .hw_value = ATH5K_RATE_CODE_6M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 90,
          .hw_value = ATH5K_RATE_CODE_9M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 120,
          .hw_value = ATH5K_RATE_CODE_12M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 180,
          .hw_value = ATH5K_RATE_CODE_18M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 240,
          .hw_value = ATH5K_RATE_CODE_24M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 360,
          .hw_value = ATH5K_RATE_CODE_36M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 480,
          .hw_value = ATH5K_RATE_CODE_48M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
        { .bitrate = 540,
          .hw_value = ATH5K_RATE_CODE_54M,
-         .flags = 0 },
+         .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+                  IEEE80211_RATE_SUPPORTS_10MHZ },
 };
 
 static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
@@ -435,11 +444,27 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
  * Called with ah->lock.
  */
 int
-ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
+ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
 {
        ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
                  "channel set, resetting (%u -> %u MHz)\n",
-                 ah->curchan->center_freq, chan->center_freq);
+                 ah->curchan->center_freq, chandef->chan->center_freq);
+
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
+               break;
+       case NL80211_CHAN_WIDTH_5:
+               ah->ah_bwmode = AR5K_BWMODE_5MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               ah->ah_bwmode = AR5K_BWMODE_10MHZ;
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
 
        /*
         * To switch channels clear any pending DMA operations;
@@ -447,7 +472,7 @@ ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
         * hardware at the new frequency, and then re-enable
         * the relevant bits of the h/w.
         */
-       return ath5k_reset(ah, chan, true);
+       return ath5k_reset(ah, chandef->chan, true);
 }
 
 void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
@@ -1400,6 +1425,16 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
 
        rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
        rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
+       switch (ah->ah_bwmode) {
+       case AR5K_BWMODE_5MHZ:
+               rxs->flag |= RX_FLAG_5MHZ;
+               break;
+       case AR5K_BWMODE_10MHZ:
+               rxs->flag |= RX_FLAG_10MHZ;
+               break;
+       default:
+               break;
+       }
 
        if (rxs->rate_idx >= 0 && rs->rs_rate ==
            ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
@@ -2507,6 +2542,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
        /* SW support for IBSS_RSN is provided by mac80211 */
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
 
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+
        /* both antennas can be configured as RX or TX */
        hw->wiphy->available_antennas_tx = 0x3;
        hw->wiphy->available_antennas_rx = 0x3;
index ca9a83c..97469d0 100644 (file)
@@ -101,7 +101,7 @@ void ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable);
 
 void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
                                        struct ieee80211_vif *vif);
-int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+int ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef);
 void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
index 9d00dab..b8d031a 100644 (file)
@@ -245,9 +245,11 @@ static ssize_t write_file_beacon(struct file *file,
        struct ath5k_hw *ah = file->private_data;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        if (strncmp(buf, "disable", 7) == 0) {
                AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
                pr_info("debugfs disable beacons\n");
@@ -345,9 +347,11 @@ static ssize_t write_file_debug(struct file *file,
        unsigned int i;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
                if (strncmp(buf, dbg_info[i].name,
                                        strlen(dbg_info[i].name)) == 0) {
@@ -448,9 +452,11 @@ static ssize_t write_file_antenna(struct file *file,
        unsigned int i;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        if (strncmp(buf, "diversity", 9) == 0) {
                ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
                pr_info("debug: enable diversity\n");
@@ -619,9 +625,11 @@ static ssize_t write_file_frameerrors(struct file *file,
        struct ath5k_statistics *st = &ah->stats;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        if (strncmp(buf, "clear", 5) == 0) {
                st->rxerr_crc = 0;
                st->rxerr_phy = 0;
@@ -766,9 +774,11 @@ static ssize_t write_file_ani(struct file *file,
        struct ath5k_hw *ah = file->private_data;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        if (strncmp(buf, "sens-low", 8) == 0) {
                ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH);
        } else if (strncmp(buf, "sens-high", 9) == 0) {
@@ -862,9 +872,11 @@ static ssize_t write_file_queue(struct file *file,
        struct ath5k_hw *ah = file->private_data;
        char buf[20];
 
-       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+       count = min_t(size_t, count, sizeof(buf) - 1);
+       if (copy_from_user(buf, userbuf, count))
                return -EFAULT;
 
+       buf[count] = '\0';
        if (strncmp(buf, "start", 5) == 0)
                ieee80211_wake_queues(ah->hw);
        else if (strncmp(buf, "stop", 4) == 0)
index 40825d4..4ee01f6 100644 (file)
@@ -202,7 +202,7 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
        mutex_lock(&ah->lock);
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               ret = ath5k_chan_set(ah, conf->chandef.chan);
+               ret = ath5k_chan_set(ah, &conf->chandef);
                if (ret < 0)
                        goto unlock;
        }
index 1f16b42..c60d36a 100644 (file)
@@ -144,11 +144,13 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band,
                sifs = AR5K_INIT_SIFS_HALF_RATE;
                preamble *= 2;
                sym_time *= 2;
+               bitrate = DIV_ROUND_UP(bitrate, 2);
                break;
        case AR5K_BWMODE_5MHZ:
                sifs = AR5K_INIT_SIFS_QUARTER_RATE;
                preamble *= 4;
                sym_time *= 4;
+               bitrate = DIV_ROUND_UP(bitrate, 4);
                break;
        default:
                sifs = AR5K_INIT_SIFS_DEFAULT_BG;
index 65fe929..0583c69 100644 (file)
@@ -566,9 +566,11 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
 {
        struct ieee80211_channel *channel = ah->ah_current_channel;
        enum ieee80211_band band;
+       struct ieee80211_supported_band *sband;
        struct ieee80211_rate *rate;
        u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
        u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
+       u32 rate_flags, i;
 
        if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
                return -EINVAL;
@@ -605,7 +607,28 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time)
        else
                band = IEEE80211_BAND_2GHZ;
 
-       rate = &ah->sbands[band].bitrates[0];
+       switch (ah->ah_bwmode) {
+       case AR5K_BWMODE_5MHZ:
+               rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ;
+               break;
+       case AR5K_BWMODE_10MHZ:
+               rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ;
+               break;
+       default:
+               rate_flags = 0;
+               break;
+       }
+       sband = &ah->sbands[band];
+       rate = NULL;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               rate = &sband->bitrates[i];
+               break;
+       }
+       if (WARN_ON(!rate))
+               return -EINVAL;
+
        ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);
 
        /* ack_tx_time includes an SIFS already */
index 6a67881..4f316bd 100644 (file)
@@ -1836,6 +1836,9 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
 
        clear_bit(WMI_READY, &ar->flag);
 
+       if (ar->fw_recovery.enable)
+               del_timer_sync(&ar->fw_recovery.hb_timer);
+
        /*
         * After wmi_shudown all WMI events will be dropped. We
         * need to cleanup the buffers allocated in AP mode and
index d4fcfca..5839fc2 100644 (file)
@@ -29,6 +29,9 @@ struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr)
        struct ath6kl_sta *conn = NULL;
        u8 i, max_conn;
 
+       if (is_zero_ether_addr(node_addr))
+               return NULL;
+
        max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
 
        for (i = 0; i < max_conn; i++) {
index acc9aa8..d67170e 100644 (file)
@@ -66,7 +66,8 @@ nla_put_failure:
        ath6kl_warn("nla_put failed on testmode rx skb!\n");
 }
 
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+                 void *data, int len)
 {
        struct ath6kl *ar = wiphy_priv(wiphy);
        struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
index fe651d6..9fbcdec 100644 (file)
@@ -20,7 +20,8 @@
 #ifdef CONFIG_NL80211_TESTMODE
 
 void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len);
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+                 void *data, int len);
 
 #else
 
@@ -29,7 +30,9 @@ static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf,
 {
 }
 
-static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+static inline int ath6kl_tm_cmd(struct wiphy *wiphy,
+                               struct wireless_dev *wdev,
+                               void *data, int len)
 {
        return 0;
 }
index d491a31..7944c25 100644 (file)
@@ -56,7 +56,7 @@ config ATH9K_AHB
 
 config ATH9K_DEBUGFS
        bool "Atheros ath9k debugging"
-       depends on ATH9K
+       depends on ATH9K && DEBUG_FS
        select MAC80211_DEBUGFS
        select RELAY
        ---help---
@@ -96,6 +96,16 @@ config ATH9K_LEGACY_RATE_CONTROL
          has to be passed to mac80211 using the module parameter,
          ieee80211_default_rc_algo.
 
+config ATH9K_RFKILL
+       bool "Atheros ath9k rfkill support" if EXPERT
+       depends on ATH9K
+       depends on RFKILL=y || RFKILL=ATH9K
+       default y
+       help
+         Say Y to have ath9k poll the RF-Kill GPIO every couple of
+         seconds. Turn off to save power, but enable it if you have
+         a platform that can toggle the RF-Kill GPIO.
+
 config ATH9K_HTC
        tristate "Atheros HTC based wireless cards support"
        depends on USB && MAC80211
index 664844c..dd1cc73 100644 (file)
 
 #include "ath9k.h"
 
-static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+/*
+ * AR9285
+ * ======
+ *
+ * EEPROM has 2 4-bit fields containing the card configuration.
+ *
+ * antdiv_ctl1:
+ * ------------
+ * bb_enable_ant_div_lnadiv : 1
+ * bb_ant_div_alt_gaintb    : 1
+ * bb_ant_div_main_gaintb   : 1
+ * bb_enable_ant_fast_div   : 1
+ *
+ * antdiv_ctl2:
+ * -----------
+ * bb_ant_div_alt_lnaconf  : 2
+ * bb_ant_div_main_lnaconf : 2
+ *
+ * The EEPROM bits are used as follows:
+ * ------------------------------------
+ *
+ * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
+ *                                 1 -> Antenna config Alt/Main uses gaintable 1
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_enable_ant_fast_div        - Enable fast antenna diversity.
+ *                                 Set in AR_PHY_CCK_DETECT.
+ *
+ * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
+ *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *                                 10=LNA1
+ *                                 01=LNA2
+ *                                 11=LNA1+LNA2
+ *                                 00=LNA1-LNA2
+ *
+ * AR9485 / AR9565 / AR9331
+ * ========================
+ *
+ * The same bits are present in the EEPROM, but the location in the
+ * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
+ *
+ * ant_div_alt_lnaconf      ==> bit 0~1
+ * ant_div_main_lnaconf     ==> bit 2~3
+ * ant_div_alt_gaintb       ==> bit 4
+ * ant_div_main_gaintb      ==> bit 5
+ * enable_ant_div_lnadiv    ==> bit 6
+ * enable_ant_fast_div      ==> bit 7
+ */
+
+static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
+                                              int alt_ratio, int maxdelta,
                                               int mindelta, int main_rssi_avg,
                                               int alt_rssi_avg, int pkt_count)
 {
-       return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-                (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
-               (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+       if (pkt_count <= 50)
+               return false;
+
+       if (alt_rssi_avg > main_rssi_avg + mindelta)
+               return true;
+
+       if (alt_ratio >= antcomb->ant_ratio2 &&
+           alt_rssi_avg >= antcomb->low_rssi_thresh &&
+           (alt_rssi_avg > main_rssi_avg + maxdelta))
+               return true;
+
+       return false;
 }
 
-static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
-                                             int curr_main_set, int curr_alt_set,
-                                             int alt_rssi_avg, int main_rssi_avg)
+static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
+                                             struct ath_ant_comb *antcomb,
+                                             int alt_ratio, int alt_rssi_avg,
+                                             int main_rssi_avg)
 {
-       bool result = false;
-       switch (div_group) {
+       bool result, set1, set2;
+
+       result = set1 = set2 = false;
+
+       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
+           conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+               set1 = true;
+
+       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
+           conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+               set2 = true;
+
+       switch (conf->div_group) {
        case 0:
                if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
                        result = true;
                break;
        case 1:
        case 2:
-               if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
-                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
-                     (alt_rssi_avg >= (main_rssi_avg - 5))) ||
-                    ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
-                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
-                     (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
-                   (alt_rssi_avg >= 4))
+               if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+                       break;
+
+               if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+                   (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
+                   (alt_ratio > antcomb->ant_ratio))
                        result = true;
-               else
-                       result = false;
+
+               break;
+       case 3:
+               if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+                       break;
+
+               if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
+                   (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
+                   (alt_ratio > antcomb->ant_ratio))
+                       result = true;
+
                break;
        }
 
@@ -108,6 +190,74 @@ static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
        }
 }
 
+static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
+                                 struct ath_hw_antcomb_conf *conf)
+{
+       /* set alt to the conf with maximun ratio */
+       if (antcomb->first_ratio && antcomb->second_ratio) {
+               if (antcomb->rssi_second > antcomb->rssi_third) {
+                       /* first alt*/
+                       if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+                               /* Set alt LNA1 or LNA2*/
+                               if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                                       conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               conf->alt_lna_conf =
+                                       antcomb->first_quick_scan_conf;
+               } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                          (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
+                       /* Set alt LNA1 or LNA2 */
+                       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               } else {
+                       /* Set alt to A+B or A-B */
+                       conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+               }
+       } else if (antcomb->first_ratio) {
+               /* first alt */
+               if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                   (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+                       /* Set alt LNA1 or LNA2 */
+                       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+       } else if (antcomb->second_ratio) {
+               /* second alt */
+               if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                   (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+                       /* Set alt LNA1 or LNA2 */
+                       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+       } else {
+               /* main is largest */
+               if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                   (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+                       /* Set alt LNA1 or LNA2 */
+                       if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       else
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       /* Set alt to A+B or A-B */
+                       conf->alt_lna_conf = antcomb->main_conf;
+       }
+}
+
 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                                       struct ath_hw_antcomb_conf *div_ant_conf,
                                       int main_rssi_avg, int alt_rssi_avg,
@@ -129,7 +279,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
 
                if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
                        /* main is LNA1 */
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -138,7 +288,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->first_ratio = false;
                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -147,11 +297,11 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->first_ratio = false;
                } else {
-                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-                             (alt_rssi_avg > main_rssi_avg +
-                              ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-                            (alt_rssi_avg > main_rssi_avg)) &&
-                           (antcomb->total_pkt_count > 50))
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               0,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
                                antcomb->first_ratio = true;
                        else
                                antcomb->first_ratio = false;
@@ -164,17 +314,21 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                antcomb->rssi_first = main_rssi_avg;
                antcomb->rssi_third = alt_rssi_avg;
 
-               if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+               switch(antcomb->second_quick_scan_conf) {
+               case ATH_ANT_DIV_COMB_LNA1:
                        antcomb->rssi_lna1 = alt_rssi_avg;
-               else if (antcomb->second_quick_scan_conf ==
-                        ATH_ANT_DIV_COMB_LNA2)
+                       break;
+               case ATH_ANT_DIV_COMB_LNA2:
                        antcomb->rssi_lna2 = alt_rssi_avg;
-               else if (antcomb->second_quick_scan_conf ==
-                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
                        if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
                                antcomb->rssi_lna2 = main_rssi_avg;
                        else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
                                antcomb->rssi_lna1 = main_rssi_avg;
+                       break;
+               default:
+                       break;
                }
 
                if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
@@ -184,7 +338,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
 
                if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -193,7 +347,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->second_ratio = false;
                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
                                                main_rssi_avg, alt_rssi_avg,
@@ -202,105 +356,18 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
                        else
                                antcomb->second_ratio = false;
                } else {
-                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-                             (alt_rssi_avg > main_rssi_avg +
-                              ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-                            (alt_rssi_avg > main_rssi_avg)) &&
-                           (antcomb->total_pkt_count > 50))
+                       if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               0,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
                                antcomb->second_ratio = true;
                        else
                                antcomb->second_ratio = false;
                }
 
-               /* set alt to the conf with maximun ratio */
-               if (antcomb->first_ratio && antcomb->second_ratio) {
-                       if (antcomb->rssi_second > antcomb->rssi_third) {
-                               /* first alt*/
-                               if ((antcomb->first_quick_scan_conf ==
-                                   ATH_ANT_DIV_COMB_LNA1) ||
-                                   (antcomb->first_quick_scan_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2))
-                                       /* Set alt LNA1 or LNA2*/
-                                       if (div_ant_conf->main_lna_conf ==
-                                           ATH_ANT_DIV_COMB_LNA2)
-                                               div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                                       else
-                                               div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                               else
-                                       /* Set alt to A+B or A-B */
-                                       div_ant_conf->alt_lna_conf =
-                                               antcomb->first_quick_scan_conf;
-                       } else if ((antcomb->second_quick_scan_conf ==
-                                  ATH_ANT_DIV_COMB_LNA1) ||
-                                  (antcomb->second_quick_scan_conf ==
-                                  ATH_ANT_DIV_COMB_LNA2)) {
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       } else {
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                       antcomb->second_quick_scan_conf;
-                       }
-               } else if (antcomb->first_ratio) {
-                       /* first alt */
-                       if ((antcomb->first_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->first_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA2))
-                                       /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                               antcomb->first_quick_scan_conf;
-               } else if (antcomb->second_ratio) {
-                               /* second alt */
-                       if ((antcomb->second_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->second_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA2))
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                               antcomb->second_quick_scan_conf;
-               } else {
-                       /* main is largest */
-                       if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf = antcomb->main_conf;
-               }
+               ath_ant_set_alt_ratio(antcomb, div_ant_conf);
+
                break;
        default:
                break;
@@ -430,8 +497,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x10: /* LNA2 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -440,15 +506,13 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x13: /* LNA2 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
                        break;
                case 0x20: /* LNA1 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -457,8 +521,7 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                        ant_conf->fast_div_bias = 0x1;
                        break;
                case 0x23: /* LNA1 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                       if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
                                ant_conf->fast_div_bias = 0x1;
                        else
                                ant_conf->fast_div_bias = 0x2;
@@ -475,6 +538,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
                default:
                        break;
                }
+
+               if (antcomb->fast_div_bias)
+                       ant_conf->fast_div_bias = antcomb->fast_div_bias;
        } else if (ant_conf->div_group == 3) {
                switch ((ant_conf->main_lna_conf << 4) |
                        ant_conf->alt_lna_conf) {
@@ -540,6 +606,138 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
        }
 }
 
+static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
+                            struct ath_hw_antcomb_conf *conf,
+                            int curr_alt_set, int alt_rssi_avg,
+                            int main_rssi_avg)
+{
+       switch (curr_alt_set) {
+       case ATH_ANT_DIV_COMB_LNA2:
+               antcomb->rssi_lna2 = alt_rssi_avg;
+               antcomb->rssi_lna1 = main_rssi_avg;
+               antcomb->scan = true;
+               /* set to A+B */
+               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case ATH_ANT_DIV_COMB_LNA1:
+               antcomb->rssi_lna1 = alt_rssi_avg;
+               antcomb->rssi_lna2 = main_rssi_avg;
+               antcomb->scan = true;
+               /* set to A+B */
+               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+               antcomb->rssi_add = alt_rssi_avg;
+               antcomb->scan = true;
+               /* set to A-B */
+               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               break;
+       case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+               antcomb->rssi_sub = alt_rssi_avg;
+               antcomb->scan = false;
+               if (antcomb->rssi_lna2 >
+                   (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+                       /* use LNA2 as main LNA */
+                       if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+                           (antcomb->rssi_add > antcomb->rssi_sub)) {
+                               /* set to A+B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       } else if (antcomb->rssi_sub >
+                                  antcomb->rssi_lna1) {
+                               /* set to A-B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       } else {
+                               /* set to LNA1 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       }
+               } else {
+                       /* use LNA1 as main LNA */
+                       if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+                           (antcomb->rssi_add > antcomb->rssi_sub)) {
+                               /* set to A+B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       } else if (antcomb->rssi_sub >
+                                  antcomb->rssi_lna1) {
+                               /* set to A-B */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       } else {
+                               /* set to LNA2 */
+                               conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                               conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
+                              struct ath_ant_comb *antcomb,
+                              int alt_ratio, int alt_rssi_avg,
+                              int main_rssi_avg, int curr_main_set,
+                              int curr_alt_set)
+{
+       bool ret = false;
+
+       if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
+                                      alt_rssi_avg, main_rssi_avg)) {
+               if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+                       /*
+                        * Switch main and alt LNA.
+                        */
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+               } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+                       div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               }
+
+               ret = true;
+       } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+                  (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+               /*
+                 Set alt to another LNA.
+               */
+               if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+                       div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+               else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+                       div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+
+               ret = true;
+       }
+
+       return ret;
+}
+
+static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
+{
+       int alt_ratio;
+
+       if (!antcomb->scan || !antcomb->alt_good)
+               return false;
+
+       if (time_after(jiffies, antcomb->scan_start_time +
+                      msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+               return true;
+
+       if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+                            antcomb->total_pkt_count);
+               if (alt_ratio < antcomb->ant_ratio)
+                       return true;
+       }
+
+       return false;
+}
+
 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
 {
        struct ath_hw_antcomb_conf div_ant_conf;
@@ -549,41 +747,46 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
        int main_rssi = rs->rs_rssi_ctl0;
        int alt_rssi = rs->rs_rssi_ctl1;
        int rx_ant_conf,  main_ant_conf;
-       bool short_scan = false;
+       bool short_scan = false, ret;
 
        rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
                       ATH_ANT_RX_MASK;
        main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
                         ATH_ANT_RX_MASK;
 
+       if (alt_rssi >= antcomb->low_rssi_thresh) {
+               antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
+               antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
+       } else {
+               antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
+               antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
+       }
+
        /* Record packet only when both main_rssi and  alt_rssi is positive */
        if (main_rssi > 0 && alt_rssi > 0) {
                antcomb->total_pkt_count++;
                antcomb->main_total_rssi += main_rssi;
                antcomb->alt_total_rssi  += alt_rssi;
+
                if (main_ant_conf == rx_ant_conf)
                        antcomb->main_recv_cnt++;
                else
                        antcomb->alt_recv_cnt++;
        }
 
-       /* Short scan check */
-       if (antcomb->scan && antcomb->alt_good) {
-               if (time_after(jiffies, antcomb->scan_start_time +
-                   msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
-                       short_scan = true;
-               else
-                       if (antcomb->total_pkt_count ==
-                           ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
-                               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
-                                           antcomb->total_pkt_count);
-                               if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
-                                       short_scan = true;
-                       }
+       if (main_ant_conf == rx_ant_conf) {
+               ANT_STAT_INC(ANT_MAIN, recv_cnt);
+               ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
+       } else {
+               ANT_STAT_INC(ANT_ALT, recv_cnt);
+               ANT_LNA_INC(ANT_ALT, rx_ant_conf);
        }
 
+       /* Short scan check */
+       short_scan = ath_ant_short_scan_check(antcomb);
+
        if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
-           rs->rs_moreaggr) && !short_scan)
+            rs->rs_moreaggr) && !short_scan)
                return;
 
        if (antcomb->total_pkt_count) {
@@ -595,15 +798,13 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
                                 antcomb->total_pkt_count);
        }
 
-
        ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
        curr_alt_set = div_ant_conf.alt_lna_conf;
        curr_main_set = div_ant_conf.main_lna_conf;
-
        antcomb->count++;
 
        if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
-               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+               if (alt_ratio > antcomb->ant_ratio) {
                        ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
                                                  main_rssi_avg);
                        antcomb->alt_good = true;
@@ -617,153 +818,47 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
        }
 
        if (!antcomb->scan) {
-               if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
-                                       alt_ratio, curr_main_set, curr_alt_set,
-                                       alt_rssi_avg, main_rssi_avg)) {
-                       if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
-                               /* Switch main and alt LNA */
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                               div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                       } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       }
-
-                       goto div_comb_done;
-               } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
-                          (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
-                       /* Set alt to another LNA */
-                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                       else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-
-                       goto div_comb_done;
-               }
-
-               if ((alt_rssi_avg < (main_rssi_avg +
-                                    div_ant_conf.lna1_lna2_delta)))
+               ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
+                                        alt_rssi_avg, main_rssi_avg,
+                                        curr_main_set, curr_alt_set);
+               if (ret)
                        goto div_comb_done;
        }
 
+       if (!antcomb->scan &&
+           (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
+               goto div_comb_done;
+
        if (!antcomb->scan_not_start) {
-               switch (curr_alt_set) {
-               case ATH_ANT_DIV_COMB_LNA2:
-                       antcomb->rssi_lna2 = alt_rssi_avg;
-                       antcomb->rssi_lna1 = main_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A+B */
-                       div_ant_conf.main_lna_conf =
-                               ATH_ANT_DIV_COMB_LNA1;
-                       div_ant_conf.alt_lna_conf  =
-                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1:
-                       antcomb->rssi_lna1 = alt_rssi_avg;
-                       antcomb->rssi_lna2 = main_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A+B */
-                       div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
-                       div_ant_conf.alt_lna_conf  =
-                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
-                       antcomb->rssi_add = alt_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A-B */
-                       div_ant_conf.alt_lna_conf =
-                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
-                       antcomb->rssi_sub = alt_rssi_avg;
-                       antcomb->scan = false;
-                       if (antcomb->rssi_lna2 >
-                           (antcomb->rssi_lna1 +
-                           ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
-                               /* use LNA2 as main LNA */
-                               if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
-                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
-                                       /* set to A+B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                               } else if (antcomb->rssi_sub >
-                                          antcomb->rssi_lna1) {
-                                       /* set to A-B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                               } else {
-                                       /* set to LNA1 */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               }
-                       } else {
-                               /* use LNA1 as main LNA */
-                               if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
-                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
-                                       /* set to A+B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                               } else if (antcomb->rssi_sub >
-                                          antcomb->rssi_lna1) {
-                                       /* set to A-B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                               } else {
-                                       /* set to LNA2 */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                               }
-                       }
-                       break;
-               default:
-                       break;
-               }
+               ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
+                                alt_rssi_avg, main_rssi_avg);
        } else {
                if (!antcomb->alt_good) {
                        antcomb->scan_not_start = false;
                        /* Set alt to another LNA */
                        if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
                                div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
+                                       ATH_ANT_DIV_COMB_LNA2;
                                div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
+                                       ATH_ANT_DIV_COMB_LNA1;
                        } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
                                div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
+                                       ATH_ANT_DIV_COMB_LNA1;
                                div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
+                                       ATH_ANT_DIV_COMB_LNA2;
                        }
                        goto div_comb_done;
                }
+               ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+                                                  main_rssi_avg, alt_rssi_avg,
+                                                  alt_ratio);
+               antcomb->quick_scan_cnt++;
        }
 
-       ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
-                                          main_rssi_avg, alt_rssi_avg,
-                                          alt_ratio);
-
-       antcomb->quick_scan_cnt++;
-
 div_comb_done:
        ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
        ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+       ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
 
        antcomb->scan_start_time = jiffies;
        antcomb->total_pkt_count = 0;
@@ -772,26 +867,3 @@ div_comb_done:
        antcomb->main_recv_cnt = 0;
        antcomb->alt_recv_cnt = 0;
 }
-
-void ath_ant_comb_update(struct ath_softc *sc)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_hw_antcomb_conf div_ant_conf;
-       u8 lna_conf;
-
-       ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
-
-       if (sc->ant_rx == 1)
-               lna_conf = ATH_ANT_DIV_COMB_LNA1;
-       else
-               lna_conf = ATH_ANT_DIV_COMB_LNA2;
-
-       div_ant_conf.main_lna_conf = lna_conf;
-       div_ant_conf.alt_lna_conf = lna_conf;
-
-       ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
-
-       if (common->antenna_diversity)
-               ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
-}
index 8dc2d08..fb61b08 100644 (file)
@@ -269,13 +269,12 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
                        if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
                                val |= AR_WA_D3_L1_DISABLE;
                } else {
-                       if (((AR_SREV_9285(ah) ||
-                             AR_SREV_9271(ah) ||
-                             AR_SREV_9287(ah)) &&
-                            (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) ||
-                           (AR_SREV_9280(ah) &&
-                            (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) {
-                               val |= AR_WA_D3_L1_DISABLE;
+                       if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
+                               if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+                                       val |= AR_WA_D3_L1_DISABLE;
+                       } else if (AR_SREV_9280(ah)) {
+                               if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+                                       val |= AR_WA_D3_L1_DISABLE;
                        }
                }
 
@@ -297,24 +296,18 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
        } else {
                if (ah->config.pcie_waen) {
                        val = ah->config.pcie_waen;
-                       if (!power_off)
-                               val &= (~AR_WA_D3_L1_DISABLE);
+                       val &= (~AR_WA_D3_L1_DISABLE);
                } else {
-                       if (AR_SREV_9285(ah) ||
-                           AR_SREV_9271(ah) ||
-                           AR_SREV_9287(ah)) {
+                       if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
                                val = AR9285_WA_DEFAULT;
-                               if (!power_off)
-                                       val &= (~AR_WA_D3_L1_DISABLE);
-                       }
-                       else if (AR_SREV_9280(ah)) {
+                               val &= (~AR_WA_D3_L1_DISABLE);
+                       } else if (AR_SREV_9280(ah)) {
                                /*
                                 * For AR9280 chips, bit 22 of 0x4004
                                 * needs to be set.
                                 */
                                val = AR9280_WA_DEFAULT;
-                               if (!power_off)
-                                       val &= (~AR_WA_D3_L1_DISABLE);
+                               val &= (~AR_WA_D3_L1_DISABLE);
                        } else {
                                val = AR_WA_DEFAULT;
                        }
index f400351..1fc1fa9 100644 (file)
@@ -555,6 +555,69 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
 }
 
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static void ar9002_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+{
+       struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
+       u8 antdiv_ctrl1, antdiv_ctrl2;
+       u32 regval;
+
+       if (enable) {
+               antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE;
+               antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE;
+
+               /*
+                * Don't disable BT ant to allow BB to control SWCOM.
+                */
+               btcoex->bt_coex_mode2 &= (~(AR_BT_DISABLE_BT_ANT));
+               REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+               REG_WRITE(ah, AR_PHY_SWITCH_COM, ATH_BT_COEX_ANT_DIV_SWITCH_COM);
+               REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+       } else {
+               /*
+                * Disable antenna diversity, use LNA1 only.
+                */
+               antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A;
+               antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A;
+
+               /*
+                * Disable BT Ant. to allow concurrent BT and WLAN receive.
+                */
+               btcoex->bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT;
+               REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+               /*
+                * Program SWCOM table to make sure RF switch always parks
+                * at BT side.
+                */
+               REG_WRITE(ah, AR_PHY_SWITCH_COM, 0);
+               REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+       }
+
+       regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+       regval &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
+        /*
+        * Clear ant_fast_div_bias [14:9] since for WB195,
+        * the main LNA is always LNA1.
+        */
+       regval &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+       regval |= SM(antdiv_ctrl1, AR_PHY_9285_ANT_DIV_CTL);
+       regval |= SM(antdiv_ctrl2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+       regval |= SM((antdiv_ctrl2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+       regval |= SM((antdiv_ctrl1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
+       regval |= SM((antdiv_ctrl1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
+       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+
+       regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+       regval &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+       regval |= SM((antdiv_ctrl1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+       REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+}
+
+#endif
+
 static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
                                    struct ath_spec_scan *param)
 {
@@ -634,5 +697,9 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
        ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
        ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;
 
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+       ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
+#endif
+
        ar9002_hw_set_nf_limits(ah);
 }
index f9eb2c3..6314ae2 100644 (file)
 #define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S    29
 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB     0x40000000
 #define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S   30
-#define AR_PHY_9285_ANT_DIV_LNA1            2
-#define AR_PHY_9285_ANT_DIV_LNA2            1
-#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2  3
-#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0
 #define AR_PHY_9285_ANT_DIV_GAINTB_0        0
 #define AR_PHY_9285_ANT_DIV_GAINTB_1        1
 
+#define ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE  0x0b
+#define ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE  0x09
+#define ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04
+#define ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09
+#define ATH_BT_COEX_ANT_DIV_SWITCH_COM      0x66666666
+
 #define AR_PHY_EXT_CCA0             0x99b8
 #define AR_PHY_EXT_CCA0_THRESH62    0x000000FF
 #define AR_PHY_EXT_CCA0_THRESH62_S  0
index d105e43..a6846ab 100644 (file)
@@ -3541,13 +3541,12 @@ static u16 ar9003_switch_com_spdt_get(struct ath_hw *ah, bool is2ghz)
        return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt);
 }
 
-
-static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
 {
        return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon);
 }
 
-static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
 {
        return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2);
 }
@@ -3561,6 +3560,7 @@ static u16 ar9003_hw_ant_ctrl_chain_get(struct ath_hw *ah, int chain,
 
 static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        int chain;
        u32 regval, value, gpio;
@@ -3614,6 +3614,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
        }
 
        value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
+       if (AR_SREV_9485(ah) && common->bt_ant_diversity) {
+               regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+               regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+
+       }
        REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
 
        if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
@@ -3645,8 +3650,11 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                regval &= (~AR_PHY_ANT_DIV_LNADIV);
                regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
 
+               if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+                       regval |= AR_ANT_DIV_ENABLE;
+
                if (AR_SREV_9565(ah)) {
-                       if (ah->shared_chain_lnadiv) {
+                       if (common->bt_ant_diversity) {
                                regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
                        } else {
                                regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
@@ -3656,10 +3664,14 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 
                REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 
-               /*enable fast_div */
+               /* enable fast_div */
                regval = REG_READ(ah, AR_PHY_CCK_DETECT);
                regval &= (~AR_FAST_DIV_ENABLE);
                regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+
+               if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+                       regval |= AR_FAST_DIV_ENABLE;
+
                REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
 
                if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
@@ -3673,9 +3685,9 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                                     AR_PHY_ANT_DIV_ALT_GAINTB |
                                     AR_PHY_ANT_DIV_MAIN_GAINTB));
                        /* by default use LNA1 for the main antenna */
-                       regval |= (AR_PHY_ANT_DIV_LNA1 <<
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
                                   AR_PHY_ANT_DIV_MAIN_LNACONF_S);
-                       regval |= (AR_PHY_ANT_DIV_LNA2 <<
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
                                   AR_PHY_ANT_DIV_ALT_LNACONF_S);
                        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
                }
@@ -3813,6 +3825,11 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
                        else
                                value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
 
+                       if (ah->config.alt_mingainidx)
+                               REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0,
+                                             AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
+                                             value);
+
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
                                      value);
index 874f657..75d4fb4 100644 (file)
@@ -334,6 +334,8 @@ struct ar9300_eeprom {
 
 s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah);
 s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah);
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz);
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz);
 
 u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
 
index d402cb3..608bb48 100644 (file)
@@ -153,7 +153,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
                if (!ah->is_clk_25mhz)
                        INIT_INI_ARRAY(&ah->iniAdditional,
                                       ar9340_1p0_radio_core_40M);
-       } else if (AR_SREV_9485_11(ah)) {
+       } else if (AR_SREV_9485_11_OR_LATER(ah)) {
                /* mac */
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
                                ar9485_1_1_mac_core);
@@ -424,7 +424,7 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9340Modes_lowest_ob_db_tx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485_modes_lowest_ob_db_tx_gain_1_1);
        else if (AR_SREV_9550(ah))
@@ -458,7 +458,7 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9340Modes_high_ob_db_tx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485Modes_high_ob_db_tx_gain_1_1);
        else if (AR_SREV_9580(ah))
@@ -492,7 +492,7 @@ static void ar9003_tx_gain_table_mode2(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9340Modes_low_ob_db_tx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485Modes_low_ob_db_tx_gain_1_1);
        else if (AR_SREV_9580(ah))
@@ -517,7 +517,7 @@ static void ar9003_tx_gain_table_mode3(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9340Modes_high_power_tx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485Modes_high_power_tx_gain_1_1);
        else if (AR_SREV_9580(ah))
@@ -552,7 +552,7 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah)
 
 static void ar9003_tx_gain_table_mode5(struct ath_hw *ah)
 {
-       if (AR_SREV_9485_11(ah))
+       if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485Modes_green_ob_db_tx_gain_1_1);
        else if (AR_SREV_9340(ah))
@@ -571,7 +571,7 @@ static void ar9003_tx_gain_table_mode6(struct ath_hw *ah)
        if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9485Modes_green_spur_ob_db_tx_gain_1_1);
        else if (AR_SREV_9580(ah))
@@ -611,7 +611,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9340Common_rx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9485_common_rx_gain_1_1);
        else if (AR_SREV_9550(ah)) {
@@ -644,7 +644,7 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
        else if (AR_SREV_9340(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                        ar9340Common_wo_xlna_rx_gain_table_1p0);
-       else if (AR_SREV_9485_11(ah))
+       else if (AR_SREV_9485_11_OR_LATER(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                        ar9485Common_wo_xlna_rx_gain_1_1);
        else if (AR_SREV_9462_21(ah))
@@ -745,16 +745,25 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah)
 static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
                                         bool power_off)
 {
+       /*
+        * Increase L1 Entry Latency. Some WB222 boards don't have
+        * this change in eeprom/OTP.
+        *
+        */
+       if (AR_SREV_9462(ah)) {
+               u32 val = ah->config.aspm_l1_fix;
+               if ((val & 0xff000000) == 0x17000000) {
+                       val &= 0x00ffffff;
+                       val |= 0x27000000;
+                       REG_WRITE(ah, 0x570c, val);
+               }
+       }
+
        /* Nothing to do on restore for 11N */
        if (!power_off /* !restore */) {
                /* set bit 19 to allow forcing of pcie core into L1 state */
                REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-
-               /* Several PCIe massages to ensure proper behaviour */
-               if (ah->config.pcie_waen)
-                       REG_WRITE(ah, AR_WA, ah->config.pcie_waen);
-               else
-                       REG_WRITE(ah, AR_WA, ah->WARegVal);
+               REG_WRITE(ah, AR_WA, ah->WARegVal);
        }
 
        /*
index 5163abd..f6c5c1b 100644 (file)
@@ -491,6 +491,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
        rxs->rs_rate = MS(rxsp->status1, AR_RxRate);
        rxs->rs_more = (rxsp->status2 & AR_RxMore) ? 1 : 0;
 
+       rxs->rs_firstaggr = (rxsp->status11 & AR_RxFirstAggr) ? 1 : 0;
        rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
        rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
        rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
index 1f694ab..46b910a 100644 (file)
@@ -632,6 +632,22 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
 
        REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
                    AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+               REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
+                         AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
+
+               if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+                                  AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
+                       ah->enabled_cals |= TX_IQ_CAL;
+               else
+                       ah->enabled_cals &= ~TX_IQ_CAL;
+
+               if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE)
+                       ah->enabled_cals |= TX_CL_CAL;
+               else
+                       ah->enabled_cals &= ~TX_CL_CAL;
+       }
 }
 
 static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@ -814,29 +830,12 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
-               REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
-                         AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
-
        ah->modes_index = modesIndex;
        ar9003_hw_override_ini(ah);
        ar9003_hw_set_channel_regs(ah, chan);
        ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
        ath9k_hw_apply_txpower(ah, chan, false);
 
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
-               if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
-                                  AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
-                       ah->enabled_cals |= TX_IQ_CAL;
-               else
-                       ah->enabled_cals &= ~TX_IQ_CAL;
-
-               if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE)
-                       ah->enabled_cals |= TX_CL_CAL;
-               else
-                       ah->enabled_cals &= ~TX_CL_CAL;
-       }
-
        return 0;
 }
 
@@ -1173,6 +1172,10 @@ skip_ws_det:
                 * is_on == 0 means MRC CCK is OFF (more noise imm)
                 */
                bool is_on = param ? 1 : 0;
+
+               if (ah->caps.rx_chainmask == 1)
+                       break;
+
                REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
                              AR_PHY_MRC_CCK_ENABLE, is_on);
                REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
@@ -1413,65 +1416,111 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 }
 
-static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
-                                                 bool enable)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
 {
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        u8 ant_div_ctl1;
        u32 regval;
 
-       if (!AR_SREV_9565(ah))
+       if (!AR_SREV_9485(ah) && !AR_SREV_9565(ah))
                return;
 
-       ah->shared_chain_lnadiv = enable;
+       if (AR_SREV_9485(ah)) {
+               regval = ar9003_hw_ant_ctrl_common_2_get(ah,
+                                                IS_CHAN_2GHZ(ah->curchan));
+               if (enable) {
+                       regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+                       regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+               }
+               REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2,
+                             AR_SWITCH_TABLE_COM2_ALL, regval);
+       }
+
        ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
 
+       /*
+        * Set MAIN/ALT LNA conf.
+        * Set MAIN/ALT gain_tb.
+        */
        regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
        regval &= (~AR_ANT_DIV_CTRL_ALL);
        regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S;
-       regval &= ~AR_PHY_ANT_DIV_LNADIV;
-       regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
-
-       if (enable)
-               regval |= AR_ANT_DIV_ENABLE;
-
        REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
 
-       regval = REG_READ(ah, AR_PHY_CCK_DETECT);
-       regval &= ~AR_FAST_DIV_ENABLE;
-       regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
-
-       if (enable)
-               regval |= AR_FAST_DIV_ENABLE;
-
-       REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
-
-       if (enable) {
-               REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
-                           (1 << AR_PHY_ANT_SW_RX_PROT_S));
-               if (ah->curchan && IS_CHAN_2GHZ(ah->curchan))
-                       REG_SET_BIT(ah, AR_PHY_RESTART,
-                                   AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
-               REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
-                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
-       } else {
-               REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
-               REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
-                           (1 << AR_PHY_ANT_SW_RX_PROT_S));
-               REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
-               REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
-                           AR_BTCOEX_WL_LNADIV_FORCE_ON);
-
+       if (AR_SREV_9485_11_OR_LATER(ah)) {
+               /*
+                * Enable LNA diversity.
+                */
                regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
-               regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
-                       AR_PHY_ANT_DIV_ALT_LNACONF |
-                       AR_PHY_ANT_DIV_MAIN_GAINTB |
-                       AR_PHY_ANT_DIV_ALT_GAINTB);
-               regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S);
-               regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S);
+               regval &= ~AR_PHY_ANT_DIV_LNADIV;
+               regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+               if (enable)
+                       regval |= AR_ANT_DIV_ENABLE;
+
                REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+
+               /*
+                * Enable fast antenna diversity.
+                */
+               regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+               regval &= ~AR_FAST_DIV_ENABLE;
+               regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+               if (enable)
+                       regval |= AR_FAST_DIV_ENABLE;
+
+               REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+
+               if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+                       regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+                       regval &= (~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+                                    AR_PHY_ANT_DIV_ALT_LNACONF |
+                                    AR_PHY_ANT_DIV_ALT_GAINTB |
+                                    AR_PHY_ANT_DIV_MAIN_GAINTB));
+                       /*
+                        * Set MAIN to LNA1 and ALT to LNA2 at the
+                        * beginning.
+                        */
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_ANT_DIV_ALT_LNACONF_S);
+                       REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+               }
+       } else if (AR_SREV_9565(ah)) {
+               if (enable) {
+                       REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   (1 << AR_PHY_ANT_SW_RX_PROT_S));
+                       if (ah->curchan && IS_CHAN_2GHZ(ah->curchan))
+                               REG_SET_BIT(ah, AR_PHY_RESTART,
+                                           AR_PHY_RESTART_ENABLE_DIV_M2FLAG);
+                       REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                   AR_BTCOEX_WL_LNADIV_FORCE_ON);
+               } else {
+                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL,
+                                   (1 << AR_PHY_ANT_SW_RX_PROT_S));
+                       REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE);
+                       REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV,
+                                   AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
+                       regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+                       regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+                                   AR_PHY_ANT_DIV_ALT_LNACONF |
+                                   AR_PHY_ANT_DIV_MAIN_GAINTB |
+                                   AR_PHY_ANT_DIV_ALT_GAINTB);
+                       regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+                       regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_ANT_DIV_ALT_LNACONF_S);
+                       REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+               }
        }
 }
 
+#endif
+
 static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
                                      struct ath9k_channel *chan,
                                      u8 *ini_reloaded)
@@ -1518,6 +1567,18 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
 
        REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
 
+       if (AR_SREV_9462_20_OR_LATER(ah)) {
+               /*
+                * CUS217 mix LNA mode.
+                */
+               if (ar9003_hw_get_rx_gain_idx(ah) == 2) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                                       1, regWrites);
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                                       modesIndex, regWrites);
+               }
+       }
+
        /*
         * For 5GHz channels requiring Fast Clock, apply
         * different modal values.
@@ -1528,7 +1589,11 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
        if (AR_SREV_9565(ah))
                REG_WRITE_ARRAY(&ah->iniModesFastClock, 1, regWrites);
 
-       REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
+       /*
+        * JAPAN regulatory.
+        */
+       if (chan->channel == 2484)
+               ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
        ah->modes_index = modesIndex;
        *ini_reloaded = true;
@@ -1631,11 +1696,14 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
 
        ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
        ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
-       ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
        ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
        ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger;
        ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait;
 
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+       ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity;
+#endif
+
        ar9003_hw_set_nf_limits(ah);
        ar9003_hw_set_radar_conf(ah);
        memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs));
index d4d39f3..6fd7523 100644 (file)
 #define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S   28
 #define AR_PHY_EXT_CCA_THRESH62 0x007F0000
 #define AR_PHY_EXT_CCA_THRESH62_S       16
+#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX    0x0000FF00
+#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX_S  8
 #define AR_PHY_EXT_MINCCA_PWR   0x01FF0000
 #define AR_PHY_EXT_MINCCA_PWR_S 16
 #define AR_PHY_EXT_CYCPWR_THR1 0x0000FE00L
 #define AR_PHY_ANT_DIV_MAIN_GAINTB              0x40000000
 #define AR_PHY_ANT_DIV_MAIN_GAINTB_S            30
 
-#define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2          0x0
-#define AR_PHY_ANT_DIV_LNA2                     0x1
-#define AR_PHY_ANT_DIV_LNA1                     0x2
-#define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2           0x3
-
 #define AR_PHY_EXTCHN_PWRTHR1   (AR_AGC_BASE + 0x2c)
 #define AR_PHY_EXT_CHN_WIN      (AR_AGC_BASE + 0x30)
 #define AR_PHY_20_40_DET_THR    (AR_AGC_BASE + 0x34)
index c1224b5..2ee35f6 100644 (file)
@@ -72,17 +72,12 @@ struct ath_config {
 /*************************/
 
 #define ATH_TXBUF_RESET(_bf) do {                              \
-               (_bf)->bf_stale = false;                        \
                (_bf)->bf_lastbf = NULL;                        \
                (_bf)->bf_next = NULL;                          \
                memset(&((_bf)->bf_state), 0,                   \
                       sizeof(struct ath_buf_state));           \
        } while (0)
 
-#define ATH_RXBUF_RESET(_bf) do {              \
-               (_bf)->bf_stale = false;        \
-       } while (0)
-
 /**
  * enum buffer_type - Buffer type flags
  *
@@ -137,7 +132,8 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 #define ATH_AGGR_ENCRYPTDELIM      10
 /* minimum h/w qdepth to be sustained to maximize aggregation */
 #define ATH_AGGR_MIN_QDEPTH        2
-#define ATH_AMPDU_SUBFRAME_DEFAULT 32
+/* minimum h/w qdepth for non-aggregated traffic */
+#define ATH_NON_AGGR_MIN_QDEPTH    8
 
 #define IEEE80211_SEQ_SEQ_SHIFT    4
 #define IEEE80211_SEQ_MAX          4096
@@ -174,12 +170,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 
 #define ATH_TX_COMPLETE_POLL_INT       1000
 
-enum ATH_AGGR_STATUS {
-       ATH_AGGR_DONE,
-       ATH_AGGR_BAW_CLOSED,
-       ATH_AGGR_LIMITED,
-};
-
 #define ATH_TXFIFO_DEPTH 8
 struct ath_txq {
        int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
@@ -201,10 +191,10 @@ struct ath_txq {
 
 struct ath_atx_ac {
        struct ath_txq *txq;
-       int sched;
        struct list_head list;
        struct list_head tid_q;
        bool clear_ps_filter;
+       bool sched;
 };
 
 struct ath_frame_info {
@@ -212,14 +202,16 @@ struct ath_frame_info {
        int framelen;
        enum ath9k_key_type keytype;
        u8 keyix;
-       u8 retries;
        u8 rtscts_rate;
+       u8 retries : 7;
+       u8 baw_tracked : 1;
 };
 
 struct ath_buf_state {
        u8 bf_type;
        u8 bfs_paprd;
        u8 ndelim;
+       bool stale;
        u16 seqno;
        unsigned long bfs_paprd_timestamp;
 };
@@ -233,7 +225,6 @@ struct ath_buf {
        void *bf_desc;                  /* virtual addr of desc */
        dma_addr_t bf_daddr;            /* physical addr of desc */
        dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */
-       bool bf_stale;
        struct ieee80211_tx_rate rates[4];
        struct ath_buf_state bf_state;
 };
@@ -241,16 +232,18 @@ struct ath_buf {
 struct ath_atx_tid {
        struct list_head list;
        struct sk_buff_head buf_q;
+       struct sk_buff_head retry_q;
        struct ath_node *an;
        struct ath_atx_ac *ac;
        unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)];
-       int bar_index;
        u16 seq_start;
        u16 seq_next;
        u16 baw_size;
-       int tidno;
+       u8 tidno;
        int baw_head;   /* first un-acked tx buffer */
        int baw_tail;   /* next unused tx buffer slot */
+
+       s8 bar_index;
        bool sched;
        bool paused;
        bool active;
@@ -262,16 +255,13 @@ struct ath_node {
        struct ieee80211_vif *vif; /* interface with which we're associated */
        struct ath_atx_tid tid[IEEE80211_NUM_TIDS];
        struct ath_atx_ac ac[IEEE80211_NUM_ACS];
-       int ps_key;
 
        u16 maxampdu;
        u8 mpdudensity;
+       s8 ps_key;
 
        bool sleeping;
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
-       struct dentry *node_stat;
-#endif
+       bool no_ps_filter;
 };
 
 struct ath_tx_control {
@@ -317,6 +307,7 @@ struct ath_rx {
        struct ath_descdma rxdma;
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
 
+       struct ath_buf *buf_hold;
        struct sk_buff *frag;
 
        u32 ampdu_ref;
@@ -367,6 +358,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 /********/
 
 struct ath_vif {
+       struct ath_node mcast_node;
        int av_bslot;
        bool primary_sta_vif;
        __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
@@ -428,6 +420,7 @@ void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_beacon(struct ath_softc *sc);
+bool ath9k_csa_is_finished(struct ath_softc *sc);
 
 /*******************/
 /* Link Monitoring */
@@ -585,19 +578,14 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
 #define ATH_ANT_DIV_COMB_MAX_COUNT 100
 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI 50
+#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50
 
 #define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
 
-enum ath9k_ant_div_comb_lna_conf {
-       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
-       ATH_ANT_DIV_COMB_LNA2,
-       ATH_ANT_DIV_COMB_LNA1,
-       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
-};
-
 struct ath_ant_comb {
        u16 count;
        u16 total_pkt_count;
@@ -614,27 +602,36 @@ struct ath_ant_comb {
        int rssi_first;
        int rssi_second;
        int rssi_third;
+       int ant_ratio;
+       int ant_ratio2;
        bool alt_good;
        int quick_scan_cnt;
-       int main_conf;
+       enum ath9k_ant_div_comb_lna_conf main_conf;
        enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
        enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
        bool first_ratio;
        bool second_ratio;
        unsigned long scan_start_time;
+
+       /*
+        * Card-specific config values.
+        */
+       int low_rssi_thresh;
+       int fast_div_bias;
 };
 
 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
-void ath_ant_comb_update(struct ath_softc *sc);
 
 /********************/
 /* Main driver core */
 /********************/
 
-#define ATH9K_PCI_CUS198 0x0001
-#define ATH9K_PCI_CUS230 0x0002
-#define ATH9K_PCI_CUS217 0x0004
-#define ATH9K_PCI_WOW    0x0008
+#define ATH9K_PCI_CUS198     0x0001
+#define ATH9K_PCI_CUS230     0x0002
+#define ATH9K_PCI_CUS217     0x0004
+#define ATH9K_PCI_WOW        0x0008
+#define ATH9K_PCI_BT_ANT_DIV 0x0010
+#define ATH9K_PCI_D3_L1_WAR  0x0020
 
 /*
  * Default cache line size, in bytes.
@@ -761,6 +758,7 @@ struct ath_softc {
 #endif
 
        struct ath_descdma txsdma;
+       struct ieee80211_vif *csa_vif;
 
        struct ath_ant_comb ant_comb;
        u8 ant_tx, ant_rx;
index 1a17732..b5c16b3 100644 (file)
@@ -291,6 +291,23 @@ void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
                (unsigned long long)tsfadjust, avp->av_bslot);
 }
 
+bool ath9k_csa_is_finished(struct ath_softc *sc)
+{
+       struct ieee80211_vif *vif;
+
+       vif = sc->csa_vif;
+       if (!vif || !vif->csa_active)
+               return false;
+
+       if (!ieee80211_csa_is_complete(vif))
+               return false;
+
+       ieee80211_csa_finish(vif);
+
+       sc->csa_vif = NULL;
+       return true;
+}
+
 void ath9k_beacon_tasklet(unsigned long data)
 {
        struct ath_softc *sc = (struct ath_softc *)data;
@@ -336,6 +353,10 @@ void ath9k_beacon_tasklet(unsigned long data)
                return;
        }
 
+       /* EDMA devices check that in the tx completion function. */
+       if (!edma && ath9k_csa_is_finished(sc))
+               return;
+
        slot = ath9k_beacon_choose_slot(sc);
        vif = sc->beacon.bslot[slot];
 
index 344fdde..d3063c2 100644 (file)
@@ -49,37 +49,40 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
 
-static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
-                                enum nl80211_channel_type channel_type)
+static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
 {
        u32 chanmode = 0;
 
-       switch (chan->band) {
+       switch (chandef->chan->band) {
        case IEEE80211_BAND_2GHZ:
-               switch (channel_type) {
-               case NL80211_CHAN_NO_HT:
-               case NL80211_CHAN_HT20:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_20:
                        chanmode = CHANNEL_G_HT20;
                        break;
-               case NL80211_CHAN_HT40PLUS:
-                       chanmode = CHANNEL_G_HT40PLUS;
+               case NL80211_CHAN_WIDTH_40:
+                       if (chandef->center_freq1 > chandef->chan->center_freq)
+                               chanmode = CHANNEL_G_HT40PLUS;
+                       else
+                               chanmode = CHANNEL_G_HT40MINUS;
                        break;
-               case NL80211_CHAN_HT40MINUS:
-                       chanmode = CHANNEL_G_HT40MINUS;
+               default:
                        break;
                }
                break;
        case IEEE80211_BAND_5GHZ:
-               switch (channel_type) {
-               case NL80211_CHAN_NO_HT:
-               case NL80211_CHAN_HT20:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+               case NL80211_CHAN_WIDTH_20:
                        chanmode = CHANNEL_A_HT20;
                        break;
-               case NL80211_CHAN_HT40PLUS:
-                       chanmode = CHANNEL_A_HT40PLUS;
+               case NL80211_CHAN_WIDTH_40:
+                       if (chandef->center_freq1 > chandef->chan->center_freq)
+                               chanmode = CHANNEL_A_HT40PLUS;
+                       else
+                               chanmode = CHANNEL_A_HT40MINUS;
                        break;
-               case NL80211_CHAN_HT40MINUS:
-                       chanmode = CHANNEL_A_HT40MINUS;
+               default:
                        break;
                }
                break;
@@ -94,13 +97,12 @@ static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
  * Update internal channel flags.
  */
 void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct ieee80211_channel *chan,
-                              enum nl80211_channel_type channel_type)
+                              struct cfg80211_chan_def *chandef)
 {
-       ichan->channel = chan->center_freq;
-       ichan->chan = chan;
+       ichan->channel = chandef->chan->center_freq;
+       ichan->chan = chandef->chan;
 
-       if (chan->band == IEEE80211_BAND_2GHZ) {
+       if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
                ichan->chanmode = CHANNEL_G;
                ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
        } else {
@@ -108,8 +110,22 @@ void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
                ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
        }
 
-       if (channel_type != NL80211_CHAN_NO_HT)
-               ichan->chanmode = ath9k_get_extchanmode(chan, channel_type);
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               ichan->channelFlags |= CHANNEL_QUARTER;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               ichan->channelFlags |= CHANNEL_HALF;
+               break;
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               break;
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_40:
+               ichan->chanmode = ath9k_get_extchanmode(chandef);
+               break;
+       default:
+               WARN_ON(1);
+       }
 }
 EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
 
@@ -125,8 +141,7 @@ struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
 
        chan_idx = curchan->hw_value;
        channel = &ah->channels[chan_idx];
-       ath9k_cmn_update_ichannel(channel, curchan,
-                                 cfg80211_get_chandef_type(&hw->conf.chandef));
+       ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
 
        return channel;
 }
index 207d069..e039bcb 100644 (file)
@@ -44,8 +44,7 @@
 
 int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
 void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
-                              struct ieee80211_channel *chan,
-                              enum nl80211_channel_type channel_type);
+                              struct cfg80211_chan_def *chandef);
 struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
                                               struct ath_hw *ah);
 int ath9k_cmn_count_streams(unsigned int chainmask, int max);
index 87454f6..c088744 100644 (file)
@@ -88,90 +88,6 @@ static const struct file_operations fops_debug = {
 
 #define DMA_BUF_LEN 1024
 
-static ssize_t read_file_tx_chainmask(struct file *file, char __user *user_buf,
-                            size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "0x%08x\n", ah->txchainmask);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_tx_chainmask(struct file *file, const char __user *user_buf,
-                            size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       unsigned long mask;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &mask))
-               return -EINVAL;
-
-       ah->txchainmask = mask;
-       ah->caps.tx_chainmask = mask;
-       return count;
-}
-
-static const struct file_operations fops_tx_chainmask = {
-       .read = read_file_tx_chainmask,
-       .write = write_file_tx_chainmask,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-
-static ssize_t read_file_rx_chainmask(struct file *file, char __user *user_buf,
-                            size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "0x%08x\n", ah->rxchainmask);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_rx_chainmask(struct file *file, const char __user *user_buf,
-                            size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       unsigned long mask;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &mask))
-               return -EINVAL;
-
-       ah->rxchainmask = mask;
-       ah->caps.rx_chainmask = mask;
-       return count;
-}
-
-static const struct file_operations fops_rx_chainmask = {
-       .read = read_file_rx_chainmask,
-       .write = write_file_rx_chainmask,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
 
 static ssize_t read_file_ani(struct file *file, char __user *user_buf,
                             size_t count, loff_t *ppos)
@@ -270,25 +186,29 @@ static const struct file_operations fops_ani = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_ant_diversity(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static ssize_t read_file_bt_ant_diversity(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        char buf[32];
        unsigned int len;
 
-       len = sprintf(buf, "%d\n", common->antenna_diversity);
+       len = sprintf(buf, "%d\n", common->bt_ant_diversity);
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 
-static ssize_t write_file_ant_diversity(struct file *file,
-                                       const char __user *user_buf,
-                                       size_t count, loff_t *ppos)
+static ssize_t write_file_bt_ant_diversity(struct file *file,
+                                          const char __user *user_buf,
+                                          size_t count, loff_t *ppos)
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       unsigned long antenna_diversity;
+       struct ath9k_hw_capabilities *pCap = &sc->sc_ah->caps;
+       unsigned long bt_ant_diversity;
        char buf[32];
        ssize_t len;
 
@@ -296,26 +216,147 @@ static ssize_t write_file_ant_diversity(struct file *file,
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
 
-       if (!AR_SREV_9565(sc->sc_ah))
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
                goto exit;
 
        buf[len] = '\0';
-       if (kstrtoul(buf, 0, &antenna_diversity))
+       if (kstrtoul(buf, 0, &bt_ant_diversity))
                return -EINVAL;
 
-       common->antenna_diversity = !!antenna_diversity;
+       common->bt_ant_diversity = !!bt_ant_diversity;
        ath9k_ps_wakeup(sc);
-       ath_ant_comb_update(sc);
-       ath_dbg(common, CONFIG, "Antenna diversity: %d\n",
-               common->antenna_diversity);
+       ath9k_hw_set_bt_ant_diversity(sc->sc_ah, common->bt_ant_diversity);
+       ath_dbg(common, CONFIG, "Enable WLAN/BT RX Antenna diversity: %d\n",
+               common->bt_ant_diversity);
        ath9k_ps_restore(sc);
 exit:
        return count;
 }
 
-static const struct file_operations fops_ant_diversity = {
-       .read = read_file_ant_diversity,
-       .write = write_file_ant_diversity,
+static const struct file_operations fops_bt_ant_diversity = {
+       .read = read_file_bt_ant_diversity,
+       .write = write_file_bt_ant_diversity,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+#endif
+
+void ath9k_debug_stat_ant(struct ath_softc *sc,
+                         struct ath_hw_antcomb_conf *div_ant_conf,
+                         int main_rssi_avg, int alt_rssi_avg)
+{
+       struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN];
+       struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT];
+
+       as_main->lna_attempt_cnt[div_ant_conf->main_lna_conf]++;
+       as_alt->lna_attempt_cnt[div_ant_conf->alt_lna_conf]++;
+
+       as_main->rssi_avg = main_rssi_avg;
+       as_alt->rssi_avg = alt_rssi_avg;
+}
+
+static ssize_t read_file_antenna_diversity(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
+       struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN];
+       struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT];
+       struct ath_hw_antcomb_conf div_ant_conf;
+       unsigned int len = 0, size = 1024;
+       ssize_t retval = 0;
+       char *buf;
+       char *lna_conf_str[4] = {"LNA1_MINUS_LNA2",
+                                "LNA2",
+                                "LNA1",
+                                "LNA1_PLUS_LNA2"};
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) {
+               len += snprintf(buf + len, size - len, "%s\n",
+                               "Antenna Diversity Combining is disabled");
+               goto exit;
+       }
+
+       ath9k_ps_wakeup(sc);
+       ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+       len += snprintf(buf + len, size - len, "Current MAIN config : %s\n",
+                       lna_conf_str[div_ant_conf.main_lna_conf]);
+       len += snprintf(buf + len, size - len, "Current ALT config  : %s\n",
+                       lna_conf_str[div_ant_conf.alt_lna_conf]);
+       len += snprintf(buf + len, size - len, "Average MAIN RSSI   : %d\n",
+                       as_main->rssi_avg);
+       len += snprintf(buf + len, size - len, "Average ALT RSSI    : %d\n\n",
+                       as_alt->rssi_avg);
+       ath9k_ps_restore(sc);
+
+       len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n");
+       len += snprintf(buf + len, size - len, "-------------------\n");
+
+       len += snprintf(buf + len, size - len, "%30s%15s\n",
+                       "MAIN", "ALT");
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "TOTAL COUNT",
+                       as_main->recv_cnt,
+                       as_alt->recv_cnt);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 + LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 - LNA2",
+                       as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                       as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+
+       len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n");
+       len += snprintf(buf + len, size - len, "--------------------\n");
+
+       len += snprintf(buf + len, size - len, "%30s%15s\n",
+                       "MAIN", "ALT");
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 + LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]);
+       len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n",
+                       "LNA1 - LNA2",
+                       as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2],
+                       as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]);
+
+exit:
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_antenna_diversity = {
+       .read = read_file_antenna_diversity,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -607,6 +648,28 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
        return retval;
 }
 
+static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq,
+                          char *buf, ssize_t size)
+{
+       ssize_t len = 0;
+
+       ath_txq_lock(sc, txq);
+
+       len += snprintf(buf + len, size - len, "%s: %d ",
+                       "qnum", txq->axq_qnum);
+       len += snprintf(buf + len, size - len, "%s: %2d ",
+                       "qdepth", txq->axq_depth);
+       len += snprintf(buf + len, size - len, "%s: %2d ",
+                       "ampdu-depth", txq->axq_ampdu_depth);
+       len += snprintf(buf + len, size - len, "%s: %3d ",
+                       "pending", txq->pending_frames);
+       len += snprintf(buf + len, size - len, "%s: %d\n",
+                       "stopped", txq->stopped);
+
+       ath_txq_unlock(sc, txq);
+       return len;
+}
+
 static ssize_t read_file_queues(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
@@ -624,24 +687,13 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf,
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                txq = sc->tx.txq_map[i];
-               len += snprintf(buf + len, size - len, "(%s): ", qname[i]);
-
-               ath_txq_lock(sc, txq);
-
-               len += snprintf(buf + len, size - len, "%s: %d ",
-                               "qnum", txq->axq_qnum);
-               len += snprintf(buf + len, size - len, "%s: %2d ",
-                               "qdepth", txq->axq_depth);
-               len += snprintf(buf + len, size - len, "%s: %2d ",
-                               "ampdu-depth", txq->axq_ampdu_depth);
-               len += snprintf(buf + len, size - len, "%s: %3d ",
-                               "pending", txq->pending_frames);
-               len += snprintf(buf + len, size - len, "%s: %d\n",
-                               "stopped", txq->stopped);
-
-               ath_txq_unlock(sc, txq);
+               len += snprintf(buf + len, size - len, "(%s):  ", qname[i]);
+               len += print_queue(sc, txq, buf + len, size - len);
        }
 
+       len += snprintf(buf + len, size - len, "(CAB): ");
+       len += print_queue(sc, sc->beacon.cabq, buf + len, size - len);
+
        if (len > size)
                len = size;
 
@@ -1589,17 +1641,7 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
                           struct dentry *dir)
 {
        struct ath_node *an = (struct ath_node *)sta->drv_priv;
-       an->node_stat = debugfs_create_file("node_stat", S_IRUGO,
-                                           dir, an, &fops_node_stat);
-}
-
-void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw,
-                             struct ieee80211_vif *vif,
-                             struct ieee80211_sta *sta,
-                             struct dentry *dir)
-{
-       struct ath_node *an = (struct ath_node *)sta->drv_priv;
-       debugfs_remove(an->node_stat);
+       debugfs_create_file("node_stat", S_IRUGO, dir, an, &fops_node_stat);
 }
 
 /* Ethtool support for get-stats */
@@ -1770,10 +1812,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_reset);
        debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_recv);
-       debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
-       debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
+       debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+                         &ah->rxchainmask);
+       debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+                         &ah->txchainmask);
        debugfs_create_file("ani", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc, &fops_ani);
        debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
@@ -1814,9 +1856,11 @@ int ath9k_init_debug(struct ath_hw *ah)
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
        debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
-       debugfs_create_file("diversity", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_ant_diversity);
+       debugfs_create_file("antenna_diversity", S_IRUSR,
+                           sc->debug.debugfs_phy, sc, &fops_antenna_diversity);
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+       debugfs_create_file("bt_ant_diversity", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_bt_ant_diversity);
        debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_btcoex);
 #endif
index fc67919..6e1556f 100644 (file)
@@ -28,9 +28,13 @@ struct fft_sample_tlv;
 #ifdef CONFIG_ATH9K_DEBUGFS
 #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
 #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++
+#define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++
+#define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++;
 #else
 #define TX_STAT_INC(q, c) do { } while (0)
 #define RESET_STAT_INC(sc, type) do { } while (0)
+#define ANT_STAT_INC(i, c) do { } while (0)
+#define ANT_LNA_INC(i, c) do { } while (0)
 #endif
 
 enum ath_reset_type {
@@ -243,11 +247,22 @@ struct ath_rx_stats {
        u32 rx_spectral;
 };
 
+#define ANT_MAIN 0
+#define ANT_ALT  1
+
+struct ath_antenna_stats {
+       u32 recv_cnt;
+       u32 rssi_avg;
+       u32 lna_recv_cnt[4];
+       u32 lna_attempt_cnt[4];
+};
+
 struct ath_stats {
        struct ath_interrupt_stats istats;
        struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
        struct ath_rx_stats rxstats;
        struct ath_dfs_stats dfs_stats;
+       struct ath_antenna_stats ant_stats[2];
        u32 reset[__RESET_TYPE_MAX];
 };
 
@@ -277,14 +292,11 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta,
                           struct dentry *dir);
-void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw,
-                             struct ieee80211_vif *vif,
-                             struct ieee80211_sta *sta,
-                             struct dentry *dir);
-
 void ath_debug_send_fft_sample(struct ath_softc *sc,
                               struct fft_sample_tlv *fft_sample);
-
+void ath9k_debug_stat_ant(struct ath_softc *sc,
+                         struct ath_hw_antcomb_conf *div_ant_conf,
+                         int main_rssi_avg, int alt_rssi_avg);
 #else
 
 #define RX_STAT_INC(c) /* NOP */
@@ -297,12 +309,10 @@ static inline int ath9k_init_debug(struct ath_hw *ah)
 static inline void ath9k_deinit_debug(struct ath_softc *sc)
 {
 }
-
 static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
                                            enum ath9k_int status)
 {
 }
-
 static inline void ath_debug_stat_tx(struct ath_softc *sc,
                                     struct ath_buf *bf,
                                     struct ath_tx_status *ts,
@@ -310,11 +320,16 @@ static inline void ath_debug_stat_tx(struct ath_softc *sc,
                                     unsigned int flags)
 {
 }
-
 static inline void ath_debug_stat_rx(struct ath_softc *sc,
                                     struct ath_rx_status *rs)
 {
 }
+static inline void ath9k_debug_stat_ant(struct ath_softc *sc,
+                                       struct ath_hw_antcomb_conf *div_ant_conf,
+                                       int main_rssi_avg, int alt_rssi_avg)
+{
+
+}
 
 #endif /* CONFIG_ATH9K_DEBUGFS */
 
index c2bfd74..9ea8e4b 100644 (file)
@@ -812,6 +812,7 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
 static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
                                         struct ath9k_channel *chan)
 {
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct modal_eep_4k_header *pModal;
        struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
        struct base_eep_header_4k *pBase = &eep->baseEepHeader;
@@ -858,6 +859,24 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
 
                REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
                regVal = REG_READ(ah, AR_PHY_CCK_DETECT);
+
+               if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+                       /*
+                        * If diversity combining is enabled,
+                        * set MAIN to LNA1 and ALT to LNA2 initially.
+                        */
+                       regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+                       regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
+                                    AR_PHY_9285_ANT_DIV_ALT_LNACONF));
+
+                       regVal |= (ATH_ANT_DIV_COMB_LNA1 <<
+                                  AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
+                       regVal |= (ATH_ANT_DIV_COMB_LNA2 <<
+                                  AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
+                       regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+                       regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
+                       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
+               }
        }
 
        if (pModal->version >= 2) {
index 5205a36..6d5d716 100644 (file)
@@ -115,10 +115,10 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,
        cmd->skb = skb;
        cmd->hif_dev = hif_dev;
 
-       usb_fill_bulk_urb(urb, hif_dev->udev,
-                        usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+       usb_fill_int_urb(urb, hif_dev->udev,
+                        usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
                         skb->data, skb->len,
-                        hif_usb_regout_cb, cmd);
+                        hif_usb_regout_cb, cmd, 1);
 
        usb_anchor_urb(urb, &hif_dev->regout_submitted);
        ret = usb_submit_urb(urb, GFP_KERNEL);
@@ -723,11 +723,11 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)
                        return;
                }
 
-               usb_fill_bulk_urb(urb, hif_dev->udev,
-                                usb_rcvbulkpipe(hif_dev->udev,
+               usb_fill_int_urb(urb, hif_dev->udev,
+                                usb_rcvintpipe(hif_dev->udev,
                                                 USB_REG_IN_PIPE),
                                 nskb->data, MAX_REG_IN_BUF_SIZE,
-                                ath9k_hif_usb_reg_in_cb, nskb);
+                                ath9k_hif_usb_reg_in_cb, nskb, 1);
        }
 
 resubmit:
@@ -909,11 +909,11 @@ static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev)
                        goto err_skb;
                }
 
-               usb_fill_bulk_urb(urb, hif_dev->udev,
-                                 usb_rcvbulkpipe(hif_dev->udev,
+               usb_fill_int_urb(urb, hif_dev->udev,
+                                 usb_rcvintpipe(hif_dev->udev,
                                                  USB_REG_IN_PIPE),
                                  skb->data, MAX_REG_IN_BUF_SIZE,
-                                 ath9k_hif_usb_reg_in_cb, skb);
+                                 ath9k_hif_usb_reg_in_cb, skb, 1);
 
                /* Anchor URB */
                usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
@@ -1031,9 +1031,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev)
 
 static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
 {
-       struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
-       struct usb_endpoint_descriptor *endp;
-       int ret, idx;
+       int ret;
 
        ret = ath9k_hif_usb_download_fw(hif_dev);
        if (ret) {
@@ -1043,20 +1041,6 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
                return ret;
        }
 
-       /* On downloading the firmware to the target, the USB descriptor of EP4
-        * is 'patched' to change the type of the endpoint to Bulk. This will
-        * bring down CPU usage during the scan period.
-        */
-       for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) {
-               endp = &alt->endpoint[idx].desc;
-               if ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                               == USB_ENDPOINT_XFER_INT) {
-                       endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK;
-                       endp->bmAttributes |= USB_ENDPOINT_XFER_BULK;
-                       endp->bInterval = 0;
-               }
-       }
-
        /* Alloc URBs */
        ret = ath9k_hif_usb_alloc_urbs(hif_dev);
        if (ret) {
@@ -1268,7 +1252,7 @@ static void ath9k_hif_usb_reboot(struct usb_device *udev)
        if (!buf)
                return;
 
-       ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE),
+       ret = usb_interrupt_msg(udev, usb_sndintpipe(udev, USB_REG_OUT_PIPE),
                           buf, 4, NULL, HZ);
        if (ret)
                dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n");
index 5c1bec1..d442581 100644 (file)
@@ -1203,16 +1203,13 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
 
        if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) {
                struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               enum nl80211_channel_type channel_type =
-                       cfg80211_get_chandef_type(&hw->conf.chandef);
                int pos = curchan->hw_value;
 
                ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
                        curchan->center_freq);
 
                ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
-                                         hw->conf.chandef.chan,
-                                         channel_type);
+                                         &hw->conf.chandef);
 
                if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
                        ath_err(common, "Unable to set channel\n");
index 14b7011..83f4927 100644 (file)
@@ -78,13 +78,16 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
        ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
 }
 
-static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
-                                                       bool enable)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
 {
-       if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv)
-               ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable);
+       if (ath9k_hw_ops(ah)->set_bt_ant_diversity)
+               ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
 }
 
+#endif
+
 /* Private hardware call ops */
 
 /* PHY ops */
index 4ca0cb0..2670bf6 100644 (file)
@@ -450,7 +450,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
        ah->config.ack_6mb = 0x0;
        ah->config.cwm_ignore_extcca = 0;
        ah->config.pcie_clock_req = 0;
-       ah->config.pcie_waen = 0;
        ah->config.analog_shiftreg = 1;
 
        for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
@@ -1069,7 +1068,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                    tx_lat += 11;
 
-               sifstime *= 2;
+               sifstime = 32;
                ack_offset = 16;
                slottime = 13;
        } else if (IS_CHAN_QUARTER_RATE(chan)) {
@@ -1079,7 +1078,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                    tx_lat += 22;
 
-               sifstime *= 4;
+               sifstime = 64;
                ack_offset = 32;
                slottime = 21;
        } else {
@@ -1116,7 +1115,6 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                ctstimeout += 48 - sifstime - ah->slottime;
        }
 
-
        ath9k_hw_set_sifs_time(ah, sifstime);
        ath9k_hw_setslottime(ah, slottime);
        ath9k_hw_set_ack_timeout(ah, acktimeout);
@@ -1496,16 +1494,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
                                    struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
+       bool band_switch = false, mode_diff = false;
+       u8 ini_reloaded = 0;
        u32 qnum;
        int r;
-       bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
-       bool band_switch, mode_diff;
-       u8 ini_reloaded;
 
-       band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
-                     (ah->curchan->channelFlags & (CHANNEL_2GHZ |
-                                                   CHANNEL_5GHZ));
-       mode_diff = (chan->chanmode != ah->curchan->chanmode);
+       if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
+               u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
+               u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
+               band_switch = (cur != new);
+               mode_diff = (chan->chanmode != ah->curchan->chanmode);
+       }
 
        for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
                if (ath9k_hw_numtxpending(ah, qnum)) {
@@ -1520,11 +1520,12 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
                return false;
        }
 
-       if (edma && (band_switch || mode_diff)) {
+       if (band_switch || mode_diff) {
                ath9k_hw_mark_phy_inactive(ah);
                udelay(5);
 
-               ath9k_hw_init_pll(ah, NULL);
+               if (band_switch)
+                       ath9k_hw_init_pll(ah, chan);
 
                if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
                        ath_err(common, "Failed to do fast channel change\n");
@@ -1541,22 +1542,21 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
        }
        ath9k_hw_set_clockrate(ah);
        ath9k_hw_apply_txpower(ah, chan, false);
-       ath9k_hw_rfbus_done(ah);
 
        if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
                ath9k_hw_set_delta_slope(ah, chan);
 
        ath9k_hw_spur_mitigate_freq(ah, chan);
 
-       if (edma && (band_switch || mode_diff)) {
-               ah->ah_flags |= AH_FASTCC;
-               if (band_switch || ini_reloaded)
-                       ah->eep_ops->set_board_values(ah, chan);
+       if (band_switch || ini_reloaded)
+               ah->eep_ops->set_board_values(ah, chan);
 
-               ath9k_hw_init_bb(ah, chan);
+       ath9k_hw_init_bb(ah, chan);
+       ath9k_hw_rfbus_done(ah);
 
-               if (band_switch || ini_reloaded)
-                       ath9k_hw_init_cal(ah, chan);
+       if (band_switch || ini_reloaded) {
+               ah->ah_flags |= AH_FASTCC;
+               ath9k_hw_init_cal(ah, chan);
                ah->ah_flags &= ~AH_FASTCC;
        }
 
@@ -1778,16 +1778,11 @@ static void ath9k_hw_init_desc(struct ath_hw *ah)
 /*
  * Fast channel change:
  * (Change synthesizer based on channel freq without resetting chip)
- *
- * Don't do FCC when
- *   - Flag is not set
- *   - Chip is just coming out of full sleep
- *   - Channel to be set is same as current channel
- *   - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel)
  */
 static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
 {
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        int ret;
 
        if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
@@ -1806,9 +1801,21 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
            (CHANNEL_HALF | CHANNEL_QUARTER))
                goto fail;
 
-       if ((chan->channelFlags & CHANNEL_ALL) !=
-           (ah->curchan->channelFlags & CHANNEL_ALL))
-               goto fail;
+       /*
+        * If cross-band fcc is not supoprted, bail out if
+        * either channelFlags or chanmode differ.
+        *
+        * chanmode will be different if the HT operating mode
+        * changes because of CSA.
+        */
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
+               if ((chan->channelFlags & CHANNEL_ALL) !=
+                   (ah->curchan->channelFlags & CHANNEL_ALL))
+                       goto fail;
+
+               if (chan->chanmode != ah->curchan->chanmode)
+                       goto fail;
+       }
 
        if (!ath9k_hw_check_alive(ah))
                goto fail;
@@ -2047,7 +2054,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ath9k_hw_apply_gpio_override(ah);
 
-       if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv)
+       if (AR_SREV_9565(ah) && common->bt_ant_diversity)
                REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
 
        return 0;
@@ -2504,7 +2511,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        else
                pCap->rts_aggr_limit = (8 * 1024);
 
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+#ifdef CONFIG_ATH9K_RFKILL
        ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT);
        if (ah->rfsilent & EEP_RFSILENT_ENABLED) {
                ah->rfkill_gpio =
@@ -2550,34 +2557,28 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
        if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
                pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
 
-       if (AR_SREV_9285(ah))
+       if (AR_SREV_9285(ah)) {
                if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
                        ant_div_ctl1 =
                                ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
-                       if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
+                       if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) {
                                pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+                               ath_info(common, "Enable LNA combining\n");
+                       }
                }
+       }
+
        if (AR_SREV_9300_20_OR_LATER(ah)) {
                if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE))
                        pCap->hw_caps |= ATH9K_HW_CAP_APM;
        }
 
-
        if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
                ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
-               /*
-                * enable the diversity-combining algorithm only when
-                * both enable_lna_div and enable_fast_div are set
-                *              Table for Diversity
-                * ant_div_alt_lnaconf          bit 0-1
-                * ant_div_main_lnaconf         bit 2-3
-                * ant_div_alt_gaintb           bit 4
-                * ant_div_main_gaintb          bit 5
-                * enable_ant_div_lnadiv        bit 6
-                * enable_ant_fast_div          bit 7
-                */
-               if ((ant_div_ctl1 >> 0x6) == 0x3)
+               if ((ant_div_ctl1 >> 0x6) == 0x3) {
                        pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+                       ath_info(common, "Enable LNA combining\n");
+               }
        }
 
        if (ath9k_hw_dfs_tested(ah))
@@ -2610,6 +2611,13 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
            ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
                        pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
 
+       /*
+        * Fast channel change across bands is available
+        * only for AR9462 and AR9565.
+        */
+       if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+               pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH;
+
        return 0;
 }
 
index cd74b3a..69a907b 100644 (file)
@@ -247,6 +247,8 @@ enum ath9k_hw_caps {
        ATH9K_HW_CAP_DFS                        = BIT(16),
        ATH9K_HW_WOW_DEVICE_CAPABLE             = BIT(17),
        ATH9K_HW_CAP_PAPRD                      = BIT(18),
+       ATH9K_HW_CAP_FCC_BAND_SWITCH            = BIT(19),
+       ATH9K_HW_CAP_BT_ANT_DIV                 = BIT(20),
 };
 
 /*
@@ -309,8 +311,11 @@ struct ath9k_ops_config {
        u16 ani_poll_interval; /* ANI poll interval in ms */
 
        /* Platform specific config */
+       u32 aspm_l1_fix;
        u32 xlna_gpio;
+       u32 ant_ctrl_comm2g_switch_enable;
        bool xatten_margin_cfg;
+       bool alt_mingainidx;
 };
 
 enum ath9k_int {
@@ -716,11 +721,14 @@ struct ath_hw_ops {
                        struct ath_hw_antcomb_conf *antconf);
        void (*antdiv_comb_conf_set)(struct ath_hw *ah,
                        struct ath_hw_antcomb_conf *antconf);
-       void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
        void (*spectral_scan_config)(struct ath_hw *ah,
                                     struct ath_spec_scan *param);
        void (*spectral_scan_trigger)(struct ath_hw *ah);
        void (*spectral_scan_wait)(struct ath_hw *ah);
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+       void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
+#endif
 };
 
 struct ath_nf_limits {
@@ -765,7 +773,6 @@ struct ath_hw {
        bool aspm_enabled;
        bool is_monitoring;
        bool need_an_top2_fixup;
-       bool shared_chain_lnadiv;
        u16 tx_trig_level;
 
        u32 nf_regs[6];
index 026a2a0..9a1f349 100644 (file)
@@ -53,9 +53,9 @@ static int ath9k_btcoex_enable;
 module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
 MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
 
-static int ath9k_enable_diversity;
-module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444);
-MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565");
+static int ath9k_bt_ant_diversity;
+module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444);
+MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity");
 
 bool is_ath9k_unloaded;
 /* We use the hw_value as an index into our private channel structure */
@@ -146,14 +146,22 @@ static struct ieee80211_rate ath9k_legacy_rates[] = {
        RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
        RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
        RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
-       RATE(60, 0x0b, 0),
-       RATE(90, 0x0f, 0),
-       RATE(120, 0x0a, 0),
-       RATE(180, 0x0e, 0),
-       RATE(240, 0x09, 0),
-       RATE(360, 0x0d, 0),
-       RATE(480, 0x08, 0),
-       RATE(540, 0x0c, 0),
+       RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                       IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                       IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
+       RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ |
+                        IEEE80211_RATE_SUPPORTS_10MHZ)),
 };
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -516,6 +524,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 static void ath9k_init_platform(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct ath_common *common = ath9k_hw_common(ah);
 
        if (common->bus_ops->ath_bus_type != ATH_PCI)
@@ -525,12 +534,27 @@ static void ath9k_init_platform(struct ath_softc *sc)
                               ATH9K_PCI_CUS230)) {
                ah->config.xlna_gpio = 9;
                ah->config.xatten_margin_cfg = true;
+               ah->config.alt_mingainidx = true;
+               ah->config.ant_ctrl_comm2g_switch_enable = 0x000BBB88;
+               sc->ant_comb.low_rssi_thresh = 20;
+               sc->ant_comb.fast_div_bias = 3;
 
                ath_info(common, "Set parameters for %s\n",
                         (sc->driver_data & ATH9K_PCI_CUS198) ?
                         "CUS198" : "CUS230");
-       } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+       }
+
+       if (sc->driver_data & ATH9K_PCI_CUS217)
                ath_info(common, "CUS217 card detected\n");
+
+       if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) {
+               pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV;
+               ath_info(common, "Set BT/WLAN RX diversity capability\n");
+       }
+
+       if (sc->driver_data & ATH9K_PCI_D3_L1_WAR) {
+               ah->config.pcie_waen = 0x0040473b;
+               ath_info(common, "Enable WAR for ASPM D3/L1\n");
        }
 }
 
@@ -584,6 +608,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 {
        struct ath9k_platform_data *pdata = sc->dev->platform_data;
        struct ath_hw *ah = NULL;
+       struct ath9k_hw_capabilities *pCap;
        struct ath_common *common;
        int ret = 0, i;
        int csz = 0;
@@ -600,6 +625,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ah->reg_ops.rmw = ath9k_reg_rmw;
        atomic_set(&ah->intr_ref_cnt, -1);
        sc->sc_ah = ah;
+       pCap = &ah->caps;
 
        sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET);
 
@@ -631,11 +657,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ath9k_init_platform(sc);
 
        /*
-        * Enable Antenna diversity only when BTCOEX is disabled
-        * and the user manually requests the feature.
+        * Enable WLAN/BT RX Antenna diversity only when:
+        *
+        * - BTCOEX is disabled.
+        * - the user manually requests the feature.
+        * - the HW cap is set using the platform data.
         */
-       if (!common->btcoex_enabled && ath9k_enable_diversity)
-               common->antenna_diversity = 1;
+       if (!common->btcoex_enabled && ath9k_bt_ant_diversity &&
+           (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
+               common->bt_ant_diversity = 1;
 
        spin_lock_init(&common->cc_lock);
 
@@ -710,13 +740,15 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
        struct ieee80211_supported_band *sband;
        struct ieee80211_channel *chan;
        struct ath_hw *ah = sc->sc_ah;
+       struct cfg80211_chan_def chandef;
        int i;
 
        sband = &sc->sbands[band];
        for (i = 0; i < sband->n_channels; i++) {
                chan = &sband->channels[i];
                ah->curchan = &ah->channels[chan->hw_value];
-               ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20);
+               cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+               ath9k_cmn_update_ichannel(ah->curchan, &chandef);
                ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
        }
 }
@@ -835,6 +867,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
        hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+       hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
 
 #ifdef CONFIG_PM_SLEEP
        if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
index fff5d3c..2f831db 100644 (file)
@@ -41,7 +41,7 @@ void ath_tx_complete_poll_work(struct work_struct *work)
                                txq->axq_tx_inprogress = true;
                        }
                }
-               ath_txq_unlock_complete(sc, txq);
+               ath_txq_unlock(sc, txq);
        }
 
        if (needreset) {
index 2ef05eb..a3eff09 100644 (file)
@@ -583,9 +583,9 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
        rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate);
        rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
 
+       rs->rs_firstaggr = (ads.ds_rxstatus8 & AR_RxFirstAggr) ? 1 : 0;
        rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
-       rs->rs_moreaggr =
-               (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
+       rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
        rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
 
        /* directly mapped flags for ieee80211_rx_status */
index b02dfce..bfccace 100644 (file)
@@ -140,6 +140,7 @@ struct ath_rx_status {
        int8_t rs_rssi_ext1;
        int8_t rs_rssi_ext2;
        u8 rs_isaggr;
+       u8 rs_firstaggr;
        u8 rs_moreaggr;
        u8 rs_num_delims;
        u8 rs_flags;
@@ -569,6 +570,7 @@ struct ar5416_desc {
 #define AR_RxAggr           0x00020000
 #define AR_PostDelimCRCErr  0x00040000
 #define AR_RxStatusRsvd71   0x3ff80000
+#define AR_RxFirstAggr      0x20000000
 #define AR_DecryptBusyErr   0x40000000
 #define AR_KeyMiss          0x80000000
 
index cb5a655..e4f6590 100644 (file)
@@ -237,9 +237,6 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                ath_restart_work(sc);
        }
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
-               ath_ant_comb_update(sc);
-
        ieee80211_wake_queues(sc->hw);
 
        return true;
@@ -965,6 +962,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_node *an = &avp->mcast_node;
 
        mutex_lock(&sc->mutex);
 
@@ -978,6 +977,12 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
+       an->sc = sc;
+       an->sta = NULL;
+       an->vif = vif;
+       an->no_ps_filter = true;
+       ath_tx_node_init(sc, an);
+
        mutex_unlock(&sc->mutex);
        return 0;
 }
@@ -1015,6 +1020,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
 
        ath_dbg(common, CONFIG, "Detach Interface\n");
 
@@ -1025,10 +1031,15 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
+       if (sc->csa_vif == vif)
+               sc->csa_vif = NULL;
+
        ath9k_ps_wakeup(sc);
        ath9k_calculate_summary_state(hw, NULL);
        ath9k_ps_restore(sc);
 
+       ath_tx_node_cleanup(sc, &avp->mcast_node);
+
        mutex_unlock(&sc->mutex);
 }
 
@@ -1192,8 +1203,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 
        if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
                struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               enum nl80211_channel_type channel_type =
-                       cfg80211_get_chandef_type(&conf->chandef);
                int pos = curchan->hw_value;
                int old_pos = -1;
                unsigned long flags;
@@ -1201,8 +1210,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                if (ah->curchan)
                        old_pos = ah->curchan - &ah->channels[0];
 
-               ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n",
-                       curchan->center_freq, channel_type);
+               ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+                       curchan->center_freq, hw->conf.chandef.width);
 
                /* update survey stats for the old channel before switching */
                spin_lock_irqsave(&common->cc_lock, flags);
@@ -1210,7 +1219,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                spin_unlock_irqrestore(&common->cc_lock, flags);
 
                ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
-                                         curchan, channel_type);
+                                         &conf->chandef);
 
                /*
                 * If the operating channel changes, change the survey in-use flags
@@ -1373,9 +1382,6 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_node *an = (struct ath_node *) sta->drv_priv;
 
-       if (!sta->ht_cap.ht_supported)
-               return;
-
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
                an->sleeping = true;
@@ -2093,7 +2099,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc,
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath9k_wow_pattern *wow_pattern = NULL;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
+       struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
        int mask_len;
        s8 i = 0;
 
@@ -2314,6 +2320,19 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(SC_OP_SCANNING, &sc->sc_flags);
 }
 
+static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct cfg80211_chan_def *chandef)
+{
+       struct ath_softc *sc = hw->priv;
+
+       /* mac80211 does not support CSA in multi-if cases (yet) */
+       if (WARN_ON(sc->csa_vif))
+               return;
+
+       sc->csa_vif = vif;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
@@ -2358,8 +2377,8 @@ struct ieee80211_ops ath9k_ops = {
 
 #if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
        .sta_add_debugfs    = ath9k_sta_add_debugfs,
-       .sta_remove_debugfs = ath9k_sta_remove_debugfs,
 #endif
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
+       .channel_switch_beacon     = ath9k_channel_switch_beacon,
 };
index c585c9b..d089a7c 100644 (file)
@@ -29,6 +29,60 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1C71),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE01F),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x11AD, /* LITEON */
+                        0x6632),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x11AD, /* LITEON */
+                        0x6642),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        PCI_VENDOR_ID_QMI,
+                        0x0306),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x185F, /* WNC */
+                        0x309D),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x10CF, /* Fujitsu */
+                        0x147C),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x10CF, /* Fujitsu */
+                        0x147D),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002A,
+                        0x10CF, /* Fujitsu */
+                        0x1536),
+         .driver_data = ATH9K_PCI_D3_L1_WAR },
+
+       /* AR9285 card for Asus */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x002B,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2C37),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+
        { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
        { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
        { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI   */
@@ -40,29 +94,106 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2086),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x1237),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2126),
-         .driver_data = ATH9K_PCI_CUS198 },
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x126A),
+         .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
 
        /* PCI-E CUS230 */
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2152),
-         .driver_data = ATH9K_PCI_CUS230 },
+         .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0032,
                         PCI_VENDOR_ID_FOXCONN,
                         0xE075),
-         .driver_data = ATH9K_PCI_CUS230 },
+         .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
+
+       /* WB225 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3119),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_ATHEROS,
+                        0x3122),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x185F, /* WNC */
+                        0x3119),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        0x185F, /* WNC */
+                        0x3027),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x4105),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x4106),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410D),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410E),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0x410F),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC706),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC680),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_SAMSUNG,
+                        0xC708),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3218),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3219),
+         .driver_data = ATH9K_PCI_BT_ANT_DIV },
 
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
@@ -229,6 +360,22 @@ static void ath_pci_aspm_init(struct ath_common *common)
                return;
        }
 
+       /*
+        * 0x70c - Ack Frequency Register.
+        *
+        * Bits 27:29 - DEFAULT_L1_ENTRANCE_LATENCY.
+        *
+        * 000 : 1 us
+        * 001 : 2 us
+        * 010 : 4 us
+        * 011 : 8 us
+        * 100 : 16 us
+        * 101 : 32 us
+        * 110/111 : 64 us
+        */
+       if (AR_SREV_9462(ah))
+               pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix);
+
        pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
        if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) {
                ah->aspm_enabled = true;
index 8b38030..4a1b992 100644 (file)
 #define AR_PHY_PLL_CONTROL 0x16180
 #define AR_PHY_PLL_MODE 0x16184
 
+enum ath9k_ant_div_comb_lna_conf {
+       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+       ATH_ANT_DIV_COMB_LNA2,
+       ATH_ANT_DIV_COMB_LNA1,
+       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
+};
+
 #endif
index 7eb1f4b..d3d7c51 100644 (file)
@@ -1275,15 +1275,21 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct ath_softc *sc = priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_rate_priv *ath_rc_priv = priv_sta;
        int i, j = 0;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef);
 
        for (i = 0; i < sband->n_bitrates; i++) {
                if (sta->supp_rates[sband->band] & BIT(i)) {
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+
                        ath_rc_priv->neg_rates.rs_rates[j]
                                = (sband->bitrates[i].bitrate * 2) / 10;
                        j++;
@@ -1313,6 +1319,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
 }
 
 static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed)
 {
@@ -1324,8 +1331,8 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
                ath_rc_init(sc, priv_sta);
 
                ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
-                       "Operating HT Bandwidth changed to: %d\n",
-                       cfg80211_get_chandef_type(&sc->hw->conf.chandef));
+                       "Operating Bandwidth changed to: %d\n",
+                       sc->hw->conf.chandef.width);
        }
 }
 
index 865e043..4ee472a 100644 (file)
@@ -42,8 +42,6 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
        struct ath_desc *ds;
        struct sk_buff *skb;
 
-       ATH_RXBUF_RESET(bf);
-
        ds = bf->bf_desc;
        ds->ds_link = 0; /* link to null */
        ds->ds_data = bf->bf_buf_addr;
@@ -70,6 +68,14 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
        sc->rx.rxlink = &ds->ds_link;
 }
 
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
+{
+       if (sc->rx.buf_hold)
+               ath_rx_buf_link(sc, sc->rx.buf_hold);
+
+       sc->rx.buf_hold = bf;
+}
+
 static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
 {
        /* XXX block beacon interrupts */
@@ -117,7 +123,6 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
 
        skb = bf->bf_mpdu;
 
-       ATH_RXBUF_RESET(bf);
        memset(skb->data, 0, ah->caps.rx_status_len);
        dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
                                ah->caps.rx_status_len, DMA_TO_DEVICE);
@@ -185,7 +190,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
 
 static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size)
 {
-       skb_queue_head_init(&rx_edma->rx_fifo);
+       __skb_queue_head_init(&rx_edma->rx_fifo);
        rx_edma->rx_fifo_hwsize = size;
 }
 
@@ -432,6 +437,7 @@ int ath_startrecv(struct ath_softc *sc)
        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;
 
+       sc->rx.buf_hold = NULL;
        sc->rx.rxlink = NULL;
        list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
                ath_rx_buf_link(sc, bf);
@@ -677,6 +683,9 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
        }
 
        bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+       if (bf == sc->rx.buf_hold)
+               return NULL;
+
        ds = bf->bf_desc;
 
        /*
@@ -755,7 +764,6 @@ static bool ath9k_rx_accept(struct ath_common *common,
        bool is_mc, is_valid_tkip, strip_mic, mic_error;
        struct ath_hw *ah = common->ah;
        __le16 fc;
-       u8 rx_status_len = ah->caps.rx_status_len;
 
        fc = hdr->frame_control;
 
@@ -777,25 +785,6 @@ static bool ath9k_rx_accept(struct ath_common *common,
            !test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
                rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
 
-       if (!rx_stats->rs_datalen) {
-               RX_STAT_INC(rx_len_err);
-               return false;
-       }
-
-        /*
-         * rs_status follows rs_datalen so if rs_datalen is too large
-         * we can take a hint that hardware corrupted it, so ignore
-         * those frames.
-         */
-       if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) {
-               RX_STAT_INC(rx_len_err);
-               return false;
-       }
-
-       /* Only use error bits from the last fragment */
-       if (rx_stats->rs_more)
-               return true;
-
        mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
                !ieee80211_has_morefrags(fc) &&
                !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
@@ -814,8 +803,6 @@ static bool ath9k_rx_accept(struct ath_common *common,
                        rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
                        mic_error = false;
                }
-               if (rx_stats->rs_status & ATH9K_RXERR_PHY)
-                       return false;
 
                if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
                    (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
@@ -865,6 +852,17 @@ static int ath9k_process_rate(struct ath_common *common,
        band = hw->conf.chandef.chan->band;
        sband = hw->wiphy->bands[band];
 
+       switch (hw->conf.chandef.width) {
+       case NL80211_CHAN_WIDTH_5:
+               rxs->flag |= RX_FLAG_5MHZ;
+               break;
+       case NL80211_CHAN_WIDTH_10:
+               rxs->flag |= RX_FLAG_10MHZ;
+               break;
+       default:
+               break;
+       }
+
        if (rx_stats->rs_rate & 0x80) {
                /* HT rate */
                rxs->flag |= RX_FLAG_HT;
@@ -898,129 +896,65 @@ static int ath9k_process_rate(struct ath_common *common,
 
 static void ath9k_process_rssi(struct ath_common *common,
                               struct ieee80211_hw *hw,
-                              struct ieee80211_hdr *hdr,
-                              struct ath_rx_status *rx_stats)
+                              struct ath_rx_status *rx_stats,
+                              struct ieee80211_rx_status *rxs)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = common->ah;
        int last_rssi;
        int rssi = rx_stats->rs_rssi;
 
-       if (!rx_stats->is_mybeacon ||
-           ((ah->opmode != NL80211_IFTYPE_STATION) &&
-            (ah->opmode != NL80211_IFTYPE_ADHOC)))
+       /*
+        * RSSI is not available for subframes in an A-MPDU.
+        */
+       if (rx_stats->rs_moreaggr) {
+               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
                return;
-
-       if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
-               ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi);
-
-       last_rssi = sc->last_rssi;
-       if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
-               rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
-       if (rssi < 0)
-               rssi = 0;
-
-       /* Update Beacon RSSI, this is used by ANI. */
-       ah->stats.avgbrssi = rssi;
-}
-
-/*
- * For Decrypt or Demic errors, we only mark packet status here and always push
- * up the frame up to let mac80211 handle the actual error case, be it no
- * decryption key or real decryption error. This let us keep statistics there.
- */
-static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
-                                  struct ieee80211_hdr *hdr,
-                                  struct ath_rx_status *rx_stats,
-                                  struct ieee80211_rx_status *rx_status,
-                                  bool *decrypt_error)
-{
-       struct ieee80211_hw *hw = sc->hw;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       bool discard_current = sc->rx.discard_next;
-
-       sc->rx.discard_next = rx_stats->rs_more;
-       if (discard_current)
-               return -EINVAL;
+       }
 
        /*
-        * everything but the rate is checked here, the rate check is done
-        * separately to avoid doing two lookups for a rate for each frame.
+        * Check if the RSSI for the last subframe in an A-MPDU
+        * or an unaggregated frame is valid.
         */
-       if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
-               return -EINVAL;
-
-       /* Only use status info from the last fragment */
-       if (rx_stats->rs_more)
-               return 0;
+       if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
+               rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+               return;
+       }
 
-       if (ath9k_process_rate(common, hw, rx_stats, rx_status))
-               return -EINVAL;
+       /*
+        * Update Beacon RSSI, this is used by ANI.
+        */
+       if (rx_stats->is_mybeacon &&
+           ((ah->opmode == NL80211_IFTYPE_STATION) ||
+            (ah->opmode == NL80211_IFTYPE_ADHOC))) {
+               ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi);
+               last_rssi = sc->last_rssi;
 
-       ath9k_process_rssi(common, hw, hdr, rx_stats);
+               if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+                       rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+               if (rssi < 0)
+                       rssi = 0;
 
-       rx_status->band = hw->conf.chandef.chan->band;
-       rx_status->freq = hw->conf.chandef.chan->center_freq;
-       rx_status->signal = ah->noise + rx_stats->rs_rssi;
-       rx_status->antenna = rx_stats->rs_antenna;
-       rx_status->flag |= RX_FLAG_MACTIME_END;
-       if (rx_stats->rs_moreaggr)
-               rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+               ah->stats.avgbrssi = rssi;
+       }
 
-       sc->rx.discard_next = false;
-       return 0;
+       rxs->signal = ah->noise + rx_stats->rs_rssi;
 }
 
-static void ath9k_rx_skb_postprocess(struct ath_common *common,
-                                    struct sk_buff *skb,
-                                    struct ath_rx_status *rx_stats,
-                                    struct ieee80211_rx_status *rxs,
-                                    bool decrypt_error)
+static void ath9k_process_tsf(struct ath_rx_status *rs,
+                             struct ieee80211_rx_status *rxs,
+                             u64 tsf)
 {
-       struct ath_hw *ah = common->ah;
-       struct ieee80211_hdr *hdr;
-       int hdrlen, padpos, padsize;
-       u8 keyix;
-       __le16 fc;
-
-       /* see if any padding is done by the hw and remove it */
-       hdr = (struct ieee80211_hdr *) skb->data;
-       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-       fc = hdr->frame_control;
-       padpos = ieee80211_hdrlen(fc);
-
-       /* The MAC header is padded to have 32-bit boundary if the
-        * packet payload is non-zero. The general calculation for
-        * padsize would take into account odd header lengths:
-        * padsize = (4 - padpos % 4) % 4; However, since only
-        * even-length headers are used, padding can only be 0 or 2
-        * bytes and we can optimize this a bit. In addition, we must
-        * not try to remove padding from short control frames that do
-        * not have payload. */
-       padsize = padpos & 3;
-       if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
-               memmove(skb->data + padsize, skb->data, padpos);
-               skb_pull(skb, padsize);
-       }
+       u32 tsf_lower = tsf & 0xffffffff;
 
-       keyix = rx_stats->rs_keyix;
+       rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp;
+       if (rs->rs_tstamp > tsf_lower &&
+           unlikely(rs->rs_tstamp - tsf_lower > 0x10000000))
+               rxs->mactime -= 0x100000000ULL;
 
-       if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
-           ieee80211_has_protected(fc)) {
-               rxs->flag |= RX_FLAG_DECRYPTED;
-       } else if (ieee80211_has_protected(fc)
-                  && !decrypt_error && skb->len >= hdrlen + 4) {
-               keyix = skb->data[hdrlen + 3] >> 6;
-
-               if (test_bit(keyix, common->keymap))
-                       rxs->flag |= RX_FLAG_DECRYPTED;
-       }
-       if (ah->sw_mgmt_crypto &&
-           (rxs->flag & RX_FLAG_DECRYPTED) &&
-           ieee80211_is_mgmt(fc))
-               /* Use software decrypt for management frames. */
-               rxs->flag &= ~RX_FLAG_DECRYPTED;
+       if (rs->rs_tstamp < tsf_lower &&
+           unlikely(tsf_lower - rs->rs_tstamp > 0x10000000))
+               rxs->mactime += 0x100000000ULL;
 }
 
 #ifdef CONFIG_ATH9K_DEBUGFS
@@ -1133,6 +1067,234 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
 #endif
 }
 
+static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (ieee80211_is_beacon(hdr->frame_control)) {
+               RX_STAT_INC(rx_beacons);
+               if (!is_zero_ether_addr(common->curbssid) &&
+                   ether_addr_equal(hdr->addr3, common->curbssid))
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * For Decrypt or Demic errors, we only mark packet status here and always push
+ * up the frame up to let mac80211 handle the actual error case, be it no
+ * decryption key or real decryption error. This let us keep statistics there.
+ */
+static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
+                                  struct sk_buff *skb,
+                                  struct ath_rx_status *rx_stats,
+                                  struct ieee80211_rx_status *rx_status,
+                                  bool *decrypt_error, u64 tsf)
+{
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hdr *hdr;
+       bool discard_current = sc->rx.discard_next;
+       int ret = 0;
+
+       /*
+        * Discard corrupt descriptors which are marked in
+        * ath_get_next_rx_buf().
+        */
+       sc->rx.discard_next = rx_stats->rs_more;
+       if (discard_current)
+               return -EINVAL;
+
+       /*
+        * Discard zero-length packets.
+        */
+       if (!rx_stats->rs_datalen) {
+               RX_STAT_INC(rx_len_err);
+               return -EINVAL;
+       }
+
+        /*
+         * rs_status follows rs_datalen so if rs_datalen is too large
+         * we can take a hint that hardware corrupted it, so ignore
+         * those frames.
+         */
+       if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) {
+               RX_STAT_INC(rx_len_err);
+               return -EINVAL;
+       }
+
+       /* Only use status info from the last fragment */
+       if (rx_stats->rs_more)
+               return 0;
+
+       /*
+        * Return immediately if the RX descriptor has been marked
+        * as corrupt based on the various error bits.
+        *
+        * This is different from the other corrupt descriptor
+        * condition handled above.
+        */
+       if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len);
+
+       ath9k_process_tsf(rx_stats, rx_status, tsf);
+       ath_debug_stat_rx(sc, rx_stats);
+
+       /*
+        * Process PHY errors and return so that the packet
+        * can be dropped.
+        */
+       if (rx_stats->rs_status & ATH9K_RXERR_PHY) {
+               ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime);
+               if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
+                       RX_STAT_INC(rx_spectral);
+
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       /*
+        * everything but the rate is checked here, the rate check is done
+        * separately to avoid doing two lookups for a rate for each frame.
+        */
+       if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       rx_stats->is_mybeacon = ath9k_is_mybeacon(sc, hdr);
+       if (rx_stats->is_mybeacon) {
+               sc->hw_busy_count = 0;
+               ath_start_rx_poll(sc, 3);
+       }
+
+       if (ath9k_process_rate(common, hw, rx_stats, rx_status)) {
+               ret =-EINVAL;
+               goto exit;
+       }
+
+       ath9k_process_rssi(common, hw, rx_stats, rx_status);
+
+       rx_status->band = hw->conf.chandef.chan->band;
+       rx_status->freq = hw->conf.chandef.chan->center_freq;
+       rx_status->antenna = rx_stats->rs_antenna;
+       rx_status->flag |= RX_FLAG_MACTIME_END;
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+       if (ieee80211_is_data_present(hdr->frame_control) &&
+           !ieee80211_is_qos_nullfunc(hdr->frame_control))
+               sc->rx.num_pkts++;
+#endif
+
+exit:
+       sc->rx.discard_next = false;
+       return ret;
+}
+
+static void ath9k_rx_skb_postprocess(struct ath_common *common,
+                                    struct sk_buff *skb,
+                                    struct ath_rx_status *rx_stats,
+                                    struct ieee80211_rx_status *rxs,
+                                    bool decrypt_error)
+{
+       struct ath_hw *ah = common->ah;
+       struct ieee80211_hdr *hdr;
+       int hdrlen, padpos, padsize;
+       u8 keyix;
+       __le16 fc;
+
+       /* see if any padding is done by the hw and remove it */
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+       fc = hdr->frame_control;
+       padpos = ieee80211_hdrlen(fc);
+
+       /* The MAC header is padded to have 32-bit boundary if the
+        * packet payload is non-zero. The general calculation for
+        * padsize would take into account odd header lengths:
+        * padsize = (4 - padpos % 4) % 4; However, since only
+        * even-length headers are used, padding can only be 0 or 2
+        * bytes and we can optimize this a bit. In addition, we must
+        * not try to remove padding from short control frames that do
+        * not have payload. */
+       padsize = padpos & 3;
+       if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
+               memmove(skb->data + padsize, skb->data, padpos);
+               skb_pull(skb, padsize);
+       }
+
+       keyix = rx_stats->rs_keyix;
+
+       if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error &&
+           ieee80211_has_protected(fc)) {
+               rxs->flag |= RX_FLAG_DECRYPTED;
+       } else if (ieee80211_has_protected(fc)
+                  && !decrypt_error && skb->len >= hdrlen + 4) {
+               keyix = skb->data[hdrlen + 3] >> 6;
+
+               if (test_bit(keyix, common->keymap))
+                       rxs->flag |= RX_FLAG_DECRYPTED;
+       }
+       if (ah->sw_mgmt_crypto &&
+           (rxs->flag & RX_FLAG_DECRYPTED) &&
+           ieee80211_is_mgmt(fc))
+               /* Use software decrypt for management frames. */
+               rxs->flag &= ~RX_FLAG_DECRYPTED;
+}
+
+/*
+ * Run the LNA combining algorithm only in these cases:
+ *
+ * Standalone WLAN cards with both LNA/Antenna diversity
+ * enabled in the EEPROM.
+ *
+ * WLAN+BT cards which are in the supported card list
+ * in ath_pci_id_table and the user has loaded the
+ * driver with "bt_ant_diversity" set to true.
+ */
+static void ath9k_antenna_check(struct ath_softc *sc,
+                               struct ath_rx_status *rs)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (!(ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB))
+               return;
+
+       /*
+        * All MPDUs in an aggregate will use the same LNA
+        * as the first MPDU.
+        */
+       if (rs->rs_isaggr && !rs->rs_firstaggr)
+               return;
+
+       /*
+        * Change the default rx antenna if rx diversity
+        * chooses the other antenna 3 times in a row.
+        */
+       if (sc->rx.defant != rs->rs_antenna) {
+               if (++sc->rx.rxotherant >= 3)
+                       ath_setdefantenna(sc, rs->rs_antenna);
+       } else {
+               sc->rx.rxotherant = 0;
+       }
+
+       if (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV) {
+               if (common->bt_ant_diversity)
+                       ath_ant_comb_scan(sc, rs);
+       } else {
+               ath_ant_comb_scan(sc, rs);
+       }
+}
+
 static void ath9k_apply_ampdu_details(struct ath_softc *sc,
        struct ath_rx_status *rs, struct ieee80211_rx_status *rxs)
 {
@@ -1159,15 +1321,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_hw *hw = sc->hw;
-       struct ieee80211_hdr *hdr;
        int retval;
        struct ath_rx_status rs;
        enum ath9k_rx_qtype qtype;
        bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
        int dma_type;
-       u8 rx_status_len = ah->caps.rx_status_len;
        u64 tsf = 0;
-       u32 tsf_lower = 0;
        unsigned long flags;
        dma_addr_t new_buf_addr;
 
@@ -1179,7 +1338,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
        qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
 
        tsf = ath9k_hw_gettsf64(ah);
-       tsf_lower = tsf & 0xffffffff;
 
        do {
                bool decrypt_error = false;
@@ -1206,55 +1364,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                else
                        hdr_skb = skb;
 
-               hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
                rxs = IEEE80211_SKB_RXCB(hdr_skb);
-               if (ieee80211_is_beacon(hdr->frame_control)) {
-                       RX_STAT_INC(rx_beacons);
-                       if (!is_zero_ether_addr(common->curbssid) &&
-                           ether_addr_equal(hdr->addr3, common->curbssid))
-                               rs.is_mybeacon = true;
-                       else
-                               rs.is_mybeacon = false;
-               }
-               else
-                       rs.is_mybeacon = false;
-
-               if (ieee80211_is_data_present(hdr->frame_control) &&
-                   !ieee80211_is_qos_nullfunc(hdr->frame_control))
-                       sc->rx.num_pkts++;
-
-               ath_debug_stat_rx(sc, &rs);
-
                memset(rxs, 0, sizeof(struct ieee80211_rx_status));
 
-               rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
-               if (rs.rs_tstamp > tsf_lower &&
-                   unlikely(rs.rs_tstamp - tsf_lower > 0x10000000))
-                       rxs->mactime -= 0x100000000ULL;
-
-               if (rs.rs_tstamp < tsf_lower &&
-                   unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
-                       rxs->mactime += 0x100000000ULL;
-
-               if (rs.rs_phyerr == ATH9K_PHYERR_RADAR)
-                       ath9k_dfs_process_phyerr(sc, hdr, &rs, rxs->mactime);
-
-               if (rs.rs_status & ATH9K_RXERR_PHY) {
-                       if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) {
-                               RX_STAT_INC(rx_spectral);
-                               goto requeue_drop_frag;
-                       }
-               }
-
-               retval = ath9k_rx_skb_preprocess(sc, hdr, &rs, rxs,
-                                                &decrypt_error);
+               retval = ath9k_rx_skb_preprocess(sc, hdr_skb, &rs, rxs,
+                                                &decrypt_error, tsf);
                if (retval)
                        goto requeue_drop_frag;
 
-               if (rs.is_mybeacon) {
-                       sc->hw_busy_count = 0;
-                       ath_start_rx_poll(sc, 3);
-               }
                /* Ensure we always have an skb to requeue once we are done
                 * processing the current buffer's skb */
                requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
@@ -1308,8 +1425,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        sc->rx.frag = skb;
                        goto requeue;
                }
-               if (rs.rs_status & ATH9K_RXERR_CORRUPT_DESC)
-                       goto requeue_drop_frag;
 
                if (sc->rx.frag) {
                        int space = skb->len - skb_tailroom(hdr_skb);
@@ -1328,22 +1443,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        skb = hdr_skb;
                }
 
-
-               if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
-
-                       /*
-                        * change the default rx antenna if rx diversity
-                        * chooses the other antenna 3 times in a row.
-                        */
-                       if (sc->rx.defant != rs.rs_antenna) {
-                               if (++sc->rx.rxotherant >= 3)
-                                       ath_setdefantenna(sc, rs.rs_antenna);
-                       } else {
-                               sc->rx.rxotherant = 0;
-                       }
-
-               }
-
                if (rxs->flag & RX_FLAG_MMIC_STRIPPED)
                        skb_trim(skb, skb->len - 8);
 
@@ -1355,8 +1454,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        ath_rx_ps(sc, skb, rs.is_mybeacon);
                spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
-               if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
-                       ath_ant_comb_scan(sc, &rs);
+               ath9k_antenna_check(sc, &rs);
 
                ath9k_apply_ampdu_details(sc, &rs, rxs);
 
@@ -1375,7 +1473,7 @@ requeue:
                if (edma) {
                        ath_rx_edma_buf_link(sc, qtype);
                } else {
-                       ath_rx_buf_link(sc, bf);
+                       ath_rx_buf_relink(sc, bf);
                        ath9k_hw_rxena(ah);
                }
        } while (1);
index 5af9744..a13b2d1 100644 (file)
 
 #define AR_SREV_9485(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
-#define AR_SREV_9485_11(_ah) \
-       (AR_SREV_9485(_ah) && \
-        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11))
+#define AR_SREV_9485_11_OR_LATER(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \
+        ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11))
 #define AR_SREV_9485_OR_LATER(_ah) \
        (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))
 
index 9279927..35b515f 100644 (file)
@@ -135,6 +135,9 @@ static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
 
 static void ath_send_bar(struct ath_atx_tid *tid, u16 seqno)
 {
+       if (!tid->an->sta)
+               return;
+
        ieee80211_send_bar(tid->an->vif, tid->an->sta->addr, tid->tidno,
                           seqno << IEEE80211_SEQ_SEQ_SHIFT);
 }
@@ -168,6 +171,71 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
        }
 }
 
+static struct ath_atx_tid *
+ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       u8 tidno = 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (ieee80211_is_data_qos(hdr->frame_control))
+               tidno = ieee80211_get_qos_ctl(hdr)[0];
+
+       tidno &= IEEE80211_QOS_CTL_TID_MASK;
+       return ATH_AN_2_TID(an, tidno);
+}
+
+static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
+{
+       return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q);
+}
+
+static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
+{
+       struct sk_buff *skb;
+
+       skb = __skb_dequeue(&tid->retry_q);
+       if (!skb)
+               skb = __skb_dequeue(&tid->buf_q);
+
+       return skb;
+}
+
+/*
+ * ath_tx_tid_change_state:
+ * - clears a-mpdu flag of previous session
+ * - force sequence number allocation to fix next BlockAck Window
+ */
+static void
+ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid)
+{
+       struct ath_txq *txq = tid->ac->txq;
+       struct ieee80211_tx_info *tx_info;
+       struct sk_buff *skb, *tskb;
+       struct ath_buf *bf;
+       struct ath_frame_info *fi;
+
+       skb_queue_walk_safe(&tid->buf_q, skb, tskb) {
+               fi = get_frame_info(skb);
+               bf = fi->bf;
+
+               tx_info = IEEE80211_SKB_CB(skb);
+               tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+               if (bf)
+                       continue;
+
+               bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+               if (!bf) {
+                       __skb_unlink(skb, &tid->buf_q);
+                       ath_txq_skb_done(sc, txq, skb);
+                       ieee80211_free_txskb(sc->hw, skb);
+                       continue;
+               }
+       }
+
+}
+
 static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 {
        struct ath_txq *txq = tid->ac->txq;
@@ -182,28 +250,22 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 
        memset(&ts, 0, sizeof(ts));
 
-       while ((skb = __skb_dequeue(&tid->buf_q))) {
+       while ((skb = __skb_dequeue(&tid->retry_q))) {
                fi = get_frame_info(skb);
                bf = fi->bf;
-
                if (!bf) {
-                       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
-                       if (!bf) {
-                               ath_txq_skb_done(sc, txq, skb);
-                               ieee80211_free_txskb(sc->hw, skb);
-                               continue;
-                       }
+                       ath_txq_skb_done(sc, txq, skb);
+                       ieee80211_free_txskb(sc->hw, skb);
+                       continue;
                }
 
-               if (fi->retries) {
-                       list_add_tail(&bf->list, &bf_head);
+               if (fi->baw_tracked) {
                        ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
-                       ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
                        sendbar = true;
-               } else {
-                       ath_set_rates(tid->an->vif, tid->an->sta, bf);
-                       ath_tx_send_normal(sc, txq, NULL, skb);
                }
+
+               list_add_tail(&bf->list, &bf_head);
+               ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
        }
 
        if (sendbar) {
@@ -232,13 +294,16 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
 }
 
 static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
-                            u16 seqno)
+                            struct ath_buf *bf)
 {
+       struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+       u16 seqno = bf->bf_state.seqno;
        int index, cindex;
 
        index  = ATH_BA_INDEX(tid->seq_start, seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
        __set_bit(cindex, tid->tx_buf);
+       fi->baw_tracked = 1;
 
        if (index >= ((tid->baw_tail - tid->baw_head) &
                (ATH_TID_MAX_BUFS - 1))) {
@@ -247,12 +312,6 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
        }
 }
 
-/*
- * TODO: For frame(s) that are in the retry state, we will reuse the
- * sequence number(s) without setting the retry bit. The
- * alternative is to give up on these and BAR the receiver's window
- * forward.
- */
 static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
                          struct ath_atx_tid *tid)
 
@@ -266,7 +325,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
        memset(&ts, 0, sizeof(ts));
        INIT_LIST_HEAD(&bf_head);
 
-       while ((skb = __skb_dequeue(&tid->buf_q))) {
+       while ((skb = ath_tid_dequeue(tid))) {
                fi = get_frame_info(skb);
                bf = fi->bf;
 
@@ -276,14 +335,8 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
                }
 
                list_add_tail(&bf->list, &bf_head);
-
-               ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
                ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
        }
-
-       tid->seq_next = tid->seq_start;
-       tid->baw_tail = tid->baw_head;
-       tid->bar_index = -1;
 }
 
 static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
@@ -403,7 +456,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        struct ieee80211_tx_rate rates[4];
        struct ath_frame_info *fi;
        int nframes;
-       u8 tidno;
        bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH);
        int i, retries;
        int bar_index = -1;
@@ -429,7 +481,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                while (bf) {
                        bf_next = bf->bf_next;
 
-                       if (!bf->bf_stale || bf_next != NULL)
+                       if (!bf->bf_state.stale || bf_next != NULL)
                                list_move_tail(&bf->list, &bf_head);
 
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0);
@@ -440,8 +492,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        }
 
        an = (struct ath_node *)sta->drv_priv;
-       tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
-       tid = ATH_AN_2_TID(an, tidno);
+       tid = ath_get_skb_tid(sc, an, skb);
        seq_first = tid->seq_start;
        isba = ts->ts_flags & ATH9K_TX_BA;
 
@@ -453,7 +504,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
         * Only BlockAcks have a TID and therefore normal Acks cannot be
         * checked
         */
-       if (isba && tidno != ts->tid)
+       if (isba && tid->tidno != ts->tid)
                txok = false;
 
        isaggr = bf_isaggr(bf);
@@ -489,7 +540,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                tx_info = IEEE80211_SKB_CB(skb);
                fi = get_frame_info(skb);
 
-               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
+               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno) ||
+                   !tid->active) {
                        /*
                         * Outside of the current BlockAck window,
                         * maybe part of a previous session
@@ -522,7 +574,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                 * not a holding desc.
                 */
                INIT_LIST_HEAD(&bf_head);
-               if (bf_next != NULL || !bf_last->bf_stale)
+               if (bf_next != NULL || !bf_last->bf_state.stale)
                        list_move_tail(&bf->list, &bf_head);
 
                if (!txpending) {
@@ -546,7 +598,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                ieee80211_sta_eosp(sta);
                        }
                        /* retry the un-acked ones */
-                       if (bf->bf_next == NULL && bf_last->bf_stale) {
+                       if (bf->bf_next == NULL && bf_last->bf_state.stale) {
                                struct ath_buf *tbf;
 
                                tbf = ath_clone_txbuf(sc, bf_last);
@@ -583,7 +635,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                if (an->sleeping)
                        ieee80211_sta_set_buffered(sta, tid->tidno, true);
 
-               skb_queue_splice(&bf_pending, &tid->buf_q);
+               skb_queue_splice_tail(&bf_pending, &tid->retry_q);
                if (!an->sleeping) {
                        ath_tx_queue_tid(txq, tid);
 
@@ -641,7 +693,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq,
        } else
                ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok);
 
-       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !flush)
+       if (!flush)
                ath_txq_schedule(sc, txq);
 }
 
@@ -815,15 +867,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
 
 static struct ath_buf *
 ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
-                       struct ath_atx_tid *tid)
+                       struct ath_atx_tid *tid, struct sk_buff_head **q)
 {
+       struct ieee80211_tx_info *tx_info;
        struct ath_frame_info *fi;
        struct sk_buff *skb;
        struct ath_buf *bf;
        u16 seqno;
 
        while (1) {
-               skb = skb_peek(&tid->buf_q);
+               *q = &tid->retry_q;
+               if (skb_queue_empty(*q))
+                       *q = &tid->buf_q;
+
+               skb = skb_peek(*q);
                if (!skb)
                        break;
 
@@ -831,14 +888,26 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
                bf = fi->bf;
                if (!fi->bf)
                        bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+               else
+                       bf->bf_state.stale = false;
 
                if (!bf) {
-                       __skb_unlink(skb, &tid->buf_q);
+                       __skb_unlink(skb, *q);
                        ath_txq_skb_done(sc, txq, skb);
                        ieee80211_free_txskb(sc->hw, skb);
                        continue;
                }
 
+               bf->bf_next = NULL;
+               bf->bf_lastbf = bf;
+
+               tx_info = IEEE80211_SKB_CB(skb);
+               tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
+               if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+                       bf->bf_state.bf_type = 0;
+                       return bf;
+               }
+
                bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR;
                seqno = bf->bf_state.seqno;
 
@@ -852,73 +921,52 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
 
                        INIT_LIST_HEAD(&bf_head);
                        list_add(&bf->list, &bf_head);
-                       __skb_unlink(skb, &tid->buf_q);
+                       __skb_unlink(skb, *q);
                        ath_tx_update_baw(sc, tid, seqno);
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
                        continue;
                }
 
-               bf->bf_next = NULL;
-               bf->bf_lastbf = bf;
                return bf;
        }
 
        return NULL;
 }
 
-static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
-                                            struct ath_txq *txq,
-                                            struct ath_atx_tid *tid,
-                                            struct list_head *bf_q,
-                                            int *aggr_len)
+static bool
+ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq,
+                struct ath_atx_tid *tid, struct list_head *bf_q,
+                struct ath_buf *bf_first, struct sk_buff_head *tid_q,
+                int *aggr_len)
 {
 #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-       struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
-       int rl = 0, nframes = 0, ndelim, prev_al = 0;
+       struct ath_buf *bf = bf_first, *bf_prev = NULL;
+       int nframes = 0, ndelim;
        u16 aggr_limit = 0, al = 0, bpad = 0,
-               al_delta, h_baw = tid->baw_size / 2;
-       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+           al_delta, h_baw = tid->baw_size / 2;
        struct ieee80211_tx_info *tx_info;
        struct ath_frame_info *fi;
        struct sk_buff *skb;
+       bool closed = false;
 
-       do {
-               bf = ath_tx_get_tid_subframe(sc, txq, tid);
-               if (!bf) {
-                       status = ATH_AGGR_BAW_CLOSED;
-                       break;
-               }
+       bf = bf_first;
+       aggr_limit = ath_lookup_rate(sc, bf, tid);
 
+       do {
                skb = bf->bf_mpdu;
                fi = get_frame_info(skb);
 
-               if (!bf_first)
-                       bf_first = bf;
-
-               if (!rl) {
-                       ath_set_rates(tid->an->vif, tid->an->sta, bf);
-                       aggr_limit = ath_lookup_rate(sc, bf, tid);
-                       rl = 1;
-               }
-
                /* do not exceed aggregation limit */
                al_delta = ATH_AGGR_DELIM_SZ + fi->framelen;
+               if (nframes) {
+                       if (aggr_limit < al + bpad + al_delta ||
+                           ath_lookup_legacy(bf) || nframes >= h_baw)
+                               break;
 
-               if (nframes &&
-                   ((aggr_limit < (al + bpad + al_delta + prev_al)) ||
-                    ath_lookup_legacy(bf))) {
-                       status = ATH_AGGR_LIMITED;
-                       break;
-               }
-
-               tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-               if (nframes && (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE))
-                       break;
-
-               /* do not exceed subframe limit */
-               if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
-                       status = ATH_AGGR_LIMITED;
-                       break;
+                       tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+                       if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
+                           !(tx_info->flags & IEEE80211_TX_CTL_AMPDU))
+                               break;
                }
 
                /* add padding for previous frame to aggregation length */
@@ -936,22 +984,37 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                bf->bf_next = NULL;
 
                /* link buffers of this frame to the aggregate */
-               if (!fi->retries)
-                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+               if (!fi->baw_tracked)
+                       ath_tx_addto_baw(sc, tid, bf);
                bf->bf_state.ndelim = ndelim;
 
-               __skb_unlink(skb, &tid->buf_q);
+               __skb_unlink(skb, tid_q);
                list_add_tail(&bf->list, bf_q);
                if (bf_prev)
                        bf_prev->bf_next = bf;
 
                bf_prev = bf;
 
-       } while (!skb_queue_empty(&tid->buf_q));
+               bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+               if (!bf) {
+                       closed = true;
+                       break;
+               }
+       } while (ath_tid_has_buffered(tid));
+
+       bf = bf_first;
+       bf->bf_lastbf = bf_prev;
+
+       if (bf == bf_prev) {
+               al = get_frame_info(bf->bf_mpdu)->framelen;
+               bf->bf_state.bf_type = BUF_AMPDU;
+       } else {
+               TX_STAT_INC(txq->axq_qnum, a_aggr);
+       }
 
        *aggr_len = al;
 
-       return status;
+       return closed;
 #undef PADBYTES
 }
 
@@ -1023,7 +1086,7 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
 }
 
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
-                            struct ath_tx_info *info, int len)
+                            struct ath_tx_info *info, int len, bool rts)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct sk_buff *skb;
@@ -1032,6 +1095,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
        const struct ieee80211_rate *rate;
        struct ieee80211_hdr *hdr;
        struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+       u32 rts_thresh = sc->hw->wiphy->rts_threshold;
        int i;
        u8 rix = 0;
 
@@ -1054,7 +1118,17 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
                rix = rates[i].idx;
                info->rates[i].Tries = rates[i].count;
 
-                   if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+               /*
+                * Handle RTS threshold for unaggregated HT frames.
+                */
+               if (bf_isampdu(bf) && !bf_isaggr(bf) &&
+                   (rates[i].flags & IEEE80211_TX_RC_MCS) &&
+                   unlikely(rts_thresh != (u32) -1)) {
+                       if (!rts_thresh || (len > rts_thresh))
+                               rts = true;
+               }
+
+               if (rts || rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                        info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
                        info->flags |= ATH9K_TXDESC_RTSENA;
                } else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
@@ -1147,6 +1221,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_buf *bf_first = NULL;
        struct ath_tx_info info;
+       u32 rts_thresh = sc->hw->wiphy->rts_threshold;
+       bool rts = false;
 
        memset(&info, 0, sizeof(info));
        info.is_first = true;
@@ -1183,7 +1259,22 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                                info.flags |= (u32) bf->bf_state.bfs_paprd <<
                                              ATH9K_TXDESC_PAPRD_S;
 
-                       ath_buf_set_rate(sc, bf, &info, len);
+                       /*
+                        * mac80211 doesn't handle RTS threshold for HT because
+                        * the decision has to be taken based on AMPDU length
+                        * and aggregation is done entirely inside ath9k.
+                        * Set the RTS/CTS flag for the first subframe based
+                        * on the threshold.
+                        */
+                       if (aggr && (bf == bf_first) &&
+                           unlikely(rts_thresh != (u32) -1)) {
+                               /*
+                                * "len" is the size of the entire AMPDU.
+                                */
+                               if (!rts_thresh || (len > rts_thresh))
+                                       rts = true;
+                       }
+                       ath_buf_set_rate(sc, bf, &info, len, rts);
                }
 
                info.buf_addr[0] = bf->bf_buf_addr;
@@ -1212,53 +1303,86 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        }
 }
 
-static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
-                             struct ath_atx_tid *tid)
+static void
+ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
+                 struct ath_atx_tid *tid, struct list_head *bf_q,
+                 struct ath_buf *bf_first, struct sk_buff_head *tid_q)
 {
-       struct ath_buf *bf;
-       enum ATH_AGGR_STATUS status;
-       struct ieee80211_tx_info *tx_info;
-       struct list_head bf_q;
-       int aggr_len;
+       struct ath_buf *bf = bf_first, *bf_prev = NULL;
+       struct sk_buff *skb;
+       int nframes = 0;
 
        do {
-               if (skb_queue_empty(&tid->buf_q))
-                       return;
+               struct ieee80211_tx_info *tx_info;
+               skb = bf->bf_mpdu;
 
-               INIT_LIST_HEAD(&bf_q);
+               nframes++;
+               __skb_unlink(skb, tid_q);
+               list_add_tail(&bf->list, bf_q);
+               if (bf_prev)
+                       bf_prev->bf_next = bf;
+               bf_prev = bf;
 
-               status = ath_tx_form_aggr(sc, txq, tid, &bf_q, &aggr_len);
+               if (nframes >= 2)
+                       break;
 
-               /*
-                * no frames picked up to be aggregated;
-                * block-ack window is not open.
-                */
-               if (list_empty(&bf_q))
+               bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+               if (!bf)
                        break;
 
-               bf = list_first_entry(&bf_q, struct ath_buf, list);
-               bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
                tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+               if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+                       break;
 
-               if (tid->ac->clear_ps_filter) {
-                       tid->ac->clear_ps_filter = false;
-                       tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-               } else {
-                       tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
-               }
+               ath_set_rates(tid->an->vif, tid->an->sta, bf);
+       } while (1);
+}
 
-               /* if only one frame, send as non-aggregate */
-               if (bf == bf->bf_lastbf) {
-                       aggr_len = get_frame_info(bf->bf_mpdu)->framelen;
-                       bf->bf_state.bf_type = BUF_AMPDU;
-               } else {
-                       TX_STAT_INC(txq->axq_qnum, a_aggr);
-               }
+static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
+                             struct ath_atx_tid *tid, bool *stop)
+{
+       struct ath_buf *bf;
+       struct ieee80211_tx_info *tx_info;
+       struct sk_buff_head *tid_q;
+       struct list_head bf_q;
+       int aggr_len = 0;
+       bool aggr, last = true;
+
+       if (!ath_tid_has_buffered(tid))
+               return false;
+
+       INIT_LIST_HEAD(&bf_q);
+
+       bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+       if (!bf)
+               return false;
+
+       tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+       aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+       if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) ||
+               (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
+               *stop = true;
+               return false;
+       }
 
-               ath_tx_fill_desc(sc, bf, txq, aggr_len);
-               ath_tx_txqaddbuf(sc, txq, &bf_q, false);
-       } while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH &&
-                status != ATH_AGGR_BAW_CLOSED);
+       ath_set_rates(tid->an->vif, tid->an->sta, bf);
+       if (aggr)
+               last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
+                                       tid_q, &aggr_len);
+       else
+               ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q);
+
+       if (list_empty(&bf_q))
+               return false;
+
+       if (tid->ac->clear_ps_filter || tid->an->no_ps_filter) {
+               tid->ac->clear_ps_filter = false;
+               tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+       }
+
+       ath_tx_fill_desc(sc, bf, txq, aggr_len);
+       ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+       return true;
 }
 
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -1282,6 +1406,9 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
                an->mpdudensity = density;
        }
 
+       /* force sequence number allocation for pending frames */
+       ath_tx_tid_change_state(sc, txtid);
+
        txtid->active = true;
        txtid->paused = true;
        *ssn = txtid->seq_start = txtid->seq_next;
@@ -1301,8 +1428,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
 
        ath_txq_lock(sc, txq);
        txtid->active = false;
-       txtid->paused = true;
+       txtid->paused = false;
        ath_tx_flush_tid(sc, txtid);
+       ath_tx_tid_change_state(sc, txtid);
        ath_txq_unlock_complete(sc, txq);
 }
 
@@ -1326,7 +1454,7 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
 
                ath_txq_lock(sc, txq);
 
-               buffered = !skb_queue_empty(&tid->buf_q);
+               buffered = ath_tid_has_buffered(tid);
 
                tid->sched = false;
                list_del(&tid->list);
@@ -1358,7 +1486,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
                ath_txq_lock(sc, txq);
                ac->clear_ps_filter = true;
 
-               if (!skb_queue_empty(&tid->buf_q) && !tid->paused) {
+               if (!tid->paused && ath_tid_has_buffered(tid)) {
                        ath_tx_queue_tid(txq, tid);
                        ath_txq_schedule(sc, txq);
                }
@@ -1383,7 +1511,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
        tid->paused = false;
 
-       if (!skb_queue_empty(&tid->buf_q)) {
+       if (ath_tid_has_buffered(tid)) {
                ath_tx_queue_tid(txq, tid);
                ath_txq_schedule(sc, txq);
        }
@@ -1403,6 +1531,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
        struct ieee80211_tx_info *info;
        struct list_head bf_q;
        struct ath_buf *bf_tail = NULL, *bf;
+       struct sk_buff_head *tid_q;
        int sent = 0;
        int i;
 
@@ -1418,15 +1547,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
                        continue;
 
                ath_txq_lock(sc, tid->ac->txq);
-               while (!skb_queue_empty(&tid->buf_q) && nframes > 0) {
-                       bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
+               while (nframes > 0) {
+                       bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q);
                        if (!bf)
                                break;
 
-                       __skb_unlink(bf->bf_mpdu, &tid->buf_q);
+                       __skb_unlink(bf->bf_mpdu, tid_q);
                        list_add_tail(&bf->list, &bf_q);
                        ath_set_rates(tid->an->vif, tid->an->sta, bf);
-                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+                       ath_tx_addto_baw(sc, tid, bf);
                        bf->bf_state.bf_type &= ~BUF_AGGR;
                        if (bf_tail)
                                bf_tail->bf_next = bf;
@@ -1436,7 +1565,7 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
                        sent++;
                        TX_STAT_INC(txq->axq_qnum, a_queued_hw);
 
-                       if (skb_queue_empty(&tid->buf_q))
+                       if (an->sta && !ath_tid_has_buffered(tid))
                                ieee80211_sta_set_buffered(an->sta, i, false);
                }
                ath_txq_unlock_complete(sc, tid->ac->txq);
@@ -1595,7 +1724,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq,
        while (!list_empty(list)) {
                bf = list_first_entry(list, struct ath_buf, list);
 
-               if (bf->bf_stale) {
+               if (bf->bf_state.stale) {
                        list_del(&bf->list);
 
                        ath_tx_return_buffer(sc, bf);
@@ -1689,25 +1818,27 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
  */
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
 {
-       struct ath_atx_ac *ac, *ac_tmp, *last_ac;
+       struct ath_atx_ac *ac, *last_ac;
        struct ath_atx_tid *tid, *last_tid;
+       bool sent = false;
 
        if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) ||
-           list_empty(&txq->axq_acq) ||
-           txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+           list_empty(&txq->axq_acq))
                return;
 
        rcu_read_lock();
 
-       ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
        last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
+       while (!list_empty(&txq->axq_acq)) {
+               bool stop = false;
 
-       list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) {
+               ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
                last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
                list_del(&ac->list);
                ac->sched = false;
 
                while (!list_empty(&ac->tid_q)) {
+
                        tid = list_first_entry(&ac->tid_q, struct ath_atx_tid,
                                               list);
                        list_del(&tid->list);
@@ -1716,17 +1847,17 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                        if (tid->paused)
                                continue;
 
-                       ath_tx_sched_aggr(sc, txq, tid);
+                       if (ath_tx_sched_aggr(sc, txq, tid, &stop))
+                               sent = true;
 
                        /*
                         * add tid to round-robin queue if more frames
                         * are pending for the tid
                         */
-                       if (!skb_queue_empty(&tid->buf_q))
+                       if (ath_tid_has_buffered(tid))
                                ath_tx_queue_tid(txq, tid);
 
-                       if (tid == last_tid ||
-                           txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+                       if (stop || tid == last_tid)
                                break;
                }
 
@@ -1735,9 +1866,17 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                        list_add_tail(&ac->list, &txq->axq_acq);
                }
 
-               if (ac == last_ac ||
-                   txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+               if (stop)
                        break;
+
+               if (ac == last_ac) {
+                       if (!sent)
+                               break;
+
+                       sent = false;
+                       last_ac = list_entry(txq->axq_acq.prev,
+                                            struct ath_atx_ac, list);
+               }
        }
 
        rcu_read_unlock();
@@ -1816,58 +1955,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
        }
 }
 
-static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq,
-                             struct ath_atx_tid *tid, struct sk_buff *skb,
-                             struct ath_tx_control *txctl)
-{
-       struct ath_frame_info *fi = get_frame_info(skb);
-       struct list_head bf_head;
-       struct ath_buf *bf;
-
-       /*
-        * Do not queue to h/w when any of the following conditions is true:
-        * - there are pending frames in software queue
-        * - the TID is currently paused for ADDBA/BAR request
-        * - seqno is not within block-ack window
-        * - h/w queue depth exceeds low water mark
-        */
-       if ((!skb_queue_empty(&tid->buf_q) || tid->paused ||
-            !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
-            txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) &&
-           txq != sc->tx.uapsdq) {
-               /*
-                * Add this frame to software queue for scheduling later
-                * for aggregation.
-                */
-               TX_STAT_INC(txq->axq_qnum, a_queued_sw);
-               __skb_queue_tail(&tid->buf_q, skb);
-               if (!txctl->an || !txctl->an->sleeping)
-                       ath_tx_queue_tid(txq, tid);
-               return;
-       }
-
-       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
-       if (!bf) {
-               ath_txq_skb_done(sc, txq, skb);
-               ieee80211_free_txskb(sc->hw, skb);
-               return;
-       }
-
-       ath_set_rates(tid->an->vif, tid->an->sta, bf);
-       bf->bf_state.bf_type = BUF_AMPDU;
-       INIT_LIST_HEAD(&bf_head);
-       list_add(&bf->list, &bf_head);
-
-       /* Add sub-frame to BAW */
-       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
-
-       /* Queue to h/w without aggregation */
-       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
-       bf->bf_lastbf = bf;
-       ath_tx_fill_desc(sc, bf, txq, fi->framelen);
-       ath_tx_txqaddbuf(sc, txq, &bf_head, false);
-}
-
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
                               struct ath_atx_tid *tid, struct sk_buff *skb)
 {
@@ -2010,6 +2097,7 @@ static int ath_tx_prepare(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_vif *avp;
        struct ath_softc *sc = hw->priv;
        int frmlen = skb->len + FCS_LEN;
        int padpos, padsize;
@@ -2017,6 +2105,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
        /* NOTE:  sta can be NULL according to net/mac80211.h */
        if (sta)
                txctl->an = (struct ath_node *)sta->drv_priv;
+       else if (vif && ieee80211_is_data(hdr->frame_control)) {
+               avp = (void *)vif->drv_priv;
+               txctl->an = &avp->mcast_node;
+       }
 
        if (info->control.hw_key)
                frmlen += info->control.hw_key->icv_len;
@@ -2066,7 +2158,6 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ath_txq *txq = txctl->txq;
        struct ath_atx_tid *tid = NULL;
        struct ath_buf *bf;
-       u8 tidno;
        int q;
        int ret;
 
@@ -2094,22 +2185,25 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                ath_txq_unlock(sc, txq);
                txq = sc->tx.uapsdq;
                ath_txq_lock(sc, txq);
-       }
-
-       if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) {
-               tidno = ieee80211_get_qos_ctl(hdr)[0] &
-                       IEEE80211_QOS_CTL_TID_MASK;
-               tid = ATH_AN_2_TID(txctl->an, tidno);
+       } else if (txctl->an &&
+                  ieee80211_is_data_present(hdr->frame_control)) {
+               tid = ath_get_skb_tid(sc, txctl->an, skb);
 
                WARN_ON(tid->ac->txq != txctl->txq);
-       }
 
-       if ((info->flags & IEEE80211_TX_CTL_AMPDU) && tid) {
+               if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
+                       tid->ac->clear_ps_filter = true;
+
                /*
-                * Try aggregation if it's a unicast data frame
-                * and the destination is HT capable.
+                * Add this frame to software queue for scheduling later
+                * for aggregation.
                 */
-               ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
+               TX_STAT_INC(txq->axq_qnum, a_queued_sw);
+               __skb_queue_tail(&tid->buf_q, skb);
+               if (!txctl->an->sleeping)
+                       ath_tx_queue_tid(txq, tid);
+
+               ath_txq_schedule(sc, txq);
                goto out;
        }
 
@@ -2168,7 +2262,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
                bf->bf_lastbf = bf;
                ath_set_rates(vif, NULL, bf);
-               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen, false);
                duration += info.rates[0].PktDuration;
                if (bf_tail)
                        bf_tail->bf_next = bf;
@@ -2372,8 +2466,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 
                if (list_empty(&txq->axq_q)) {
                        txq->axq_link = NULL;
-                       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
-                               ath_txq_schedule(sc, txq);
+                       ath_txq_schedule(sc, txq);
                        break;
                }
                bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
@@ -2387,7 +2480,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                 * it with the STALE flag.
                 */
                bf_held = NULL;
-               if (bf->bf_stale) {
+               if (bf->bf_state.stale) {
                        bf_held = bf;
                        if (list_is_last(&bf_held->list, &txq->axq_q))
                                break;
@@ -2411,7 +2504,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                 * however leave the last descriptor back as the holding
                 * descriptor for hw.
                 */
-               lastbf->bf_stale = true;
+               lastbf->bf_state.stale = true;
                INIT_LIST_HEAD(&bf_head);
                if (!list_is_singular(&lastbf->list))
                        list_cut_position(&bf_head,
@@ -2466,6 +2559,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                if (ts.qid == sc->beacon.beaconq) {
                        sc->beacon.tx_processed = true;
                        sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
+
+                       ath9k_csa_is_finished(sc);
                        continue;
                }
 
@@ -2482,7 +2577,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                }
 
                bf = list_first_entry(fifo_list, struct ath_buf, list);
-               if (bf->bf_stale) {
+               if (bf->bf_state.stale) {
                        list_del(&bf->list);
                        ath_tx_return_buffer(sc, bf);
                        bf = list_first_entry(fifo_list, struct ath_buf, list);
@@ -2504,7 +2599,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                                ath_tx_txqaddbuf(sc, txq, &bf_q, true);
                        }
                } else {
-                       lastbf->bf_stale = true;
+                       lastbf->bf_state.stale = true;
                        if (bf != lastbf)
                                list_cut_position(&bf_head, fifo_list,
                                                  lastbf->list.prev);
@@ -2595,6 +2690,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
                tid->paused    = false;
                tid->active        = false;
                __skb_queue_head_init(&tid->buf_q);
+               __skb_queue_head_init(&tid->retry_q);
                acno = TID_TO_WME_AC(tidno);
                tid->ac = &an->ac[acno];
        }
@@ -2602,6 +2698,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
        for (acno = 0, ac = &an->ac[acno];
             acno < IEEE80211_NUM_ACS; acno++, ac++) {
                ac->sched    = false;
+               ac->clear_ps_filter = true;
                ac->txq = sc->tx.txq_map[acno];
                INIT_LIST_HEAD(&ac->tid_q);
        }
index f891d51..990dd42 100644 (file)
@@ -11,9 +11,6 @@ wil6210-y += txrx.o
 wil6210-y += debug.o
 wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
 
-ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
-       subdir-ccflags-y += -Werror
-endif
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
 
index ab63676..1caa319 100644 (file)
@@ -51,7 +51,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
                        if ((i % 64) == 0 && (i != 0))
                                seq_printf(s, "\n");
                        seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
-                                       "S" : (vring->ctx[i] ? "H" : "h"));
+                                       "S" : (vring->ctx[i].skb ? "H" : "h"));
                }
                seq_printf(s, "\n");
        }
@@ -406,7 +406,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
                volatile struct vring_tx_desc *d =
                                &(vring->va[dbg_txdesc_index].tx);
                volatile u32 *u = (volatile u32 *)d;
-               struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
+               struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
 
                seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
                seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
index 29dd1e5..717178f 100644 (file)
@@ -127,6 +127,8 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
 
        ndev->netdev_ops = &wil_netdev_ops;
        ndev->ieee80211_ptr = wdev;
+       ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
        SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
        wdev->netdev = ndev;
 
index eff1239..e59239d 100644 (file)
@@ -37,36 +37,40 @@ static inline void trace_ ## name(proto) {}
 #endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
 
 DECLARE_EVENT_CLASS(wil6210_wmi,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
 
-       TP_ARGS(id, buf, buf_len),
+       TP_ARGS(wmi, buf, buf_len),
 
        TP_STRUCT__entry(
+               __field(u8, mid)
                __field(u16, id)
+               __field(u32, timestamp)
                __field(u16, buf_len)
                __dynamic_array(u8, buf, buf_len)
        ),
 
        TP_fast_assign(
-               __entry->id = id;
+               __entry->mid = wmi->mid;
+               __entry->id = le16_to_cpu(wmi->id);
+               __entry->timestamp = le32_to_cpu(wmi->timestamp);
                __entry->buf_len = buf_len;
                memcpy(__get_dynamic_array(buf), buf, buf_len);
        ),
 
        TP_printk(
-               "id 0x%04x len %d",
-               __entry->id, __entry->buf_len
+               "MID %d id 0x%04x len %d timestamp %d",
+               __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
        )
 );
 
 DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
-       TP_ARGS(id, buf, buf_len)
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+       TP_ARGS(wmi, buf, buf_len)
 );
 
 DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
-       TP_PROTO(u16 id, void *buf, u16 buf_len),
-       TP_ARGS(id, buf, buf_len)
+       TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+       TP_ARGS(wmi, buf, buf_len)
 );
 
 #define WIL6210_MSG_MAX (200)
index d240b24..d505b26 100644 (file)
@@ -18,6 +18,9 @@
 #include <net/ieee80211_radiotap.h>
 #include <linux/if_arp.h>
 #include <linux/moduleparam.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
 
 #include "wil6210.h"
 #include "wmi.h"
@@ -70,7 +73,7 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 
        vring->swhead = 0;
        vring->swtail = 0;
-       vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+       vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL);
        if (!vring->ctx) {
                vring->va = NULL;
                return -ENOMEM;
@@ -108,39 +111,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
 
        while (!wil_vring_is_empty(vring)) {
                dma_addr_t pa;
-               struct sk_buff *skb;
                u16 dmalen;
+               struct wil_ctx *ctx;
 
                if (tx) {
                        struct vring_tx_desc dd, *d = &dd;
                        volatile struct vring_tx_desc *_d =
                                        &vring->va[vring->swtail].tx;
 
+                       ctx = &vring->ctx[vring->swtail];
                        *d = *_d;
                        pa = wil_desc_addr(&d->dma.addr);
                        dmalen = le16_to_cpu(d->dma.length);
-                       skb = vring->ctx[vring->swtail];
-                       if (skb) {
-                               dma_unmap_single(dev, pa, dmalen,
-                                                DMA_TO_DEVICE);
-                               dev_kfree_skb_any(skb);
-                               vring->ctx[vring->swtail] = NULL;
-                       } else {
+                       if (vring->ctx[vring->swtail].mapped_as_page) {
                                dma_unmap_page(dev, pa, dmalen,
                                               DMA_TO_DEVICE);
+                       } else {
+                               dma_unmap_single(dev, pa, dmalen,
+                                                DMA_TO_DEVICE);
                        }
+                       if (ctx->skb)
+                               dev_kfree_skb_any(ctx->skb);
                        vring->swtail = wil_vring_next_tail(vring);
                } else { /* rx */
                        struct vring_rx_desc dd, *d = &dd;
                        volatile struct vring_rx_desc *_d =
-                                       &vring->va[vring->swtail].rx;
+                                       &vring->va[vring->swhead].rx;
 
+                       ctx = &vring->ctx[vring->swhead];
                        *d = *_d;
                        pa = wil_desc_addr(&d->dma.addr);
                        dmalen = le16_to_cpu(d->dma.length);
-                       skb = vring->ctx[vring->swhead];
                        dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
-                       kfree_skb(skb);
+                       kfree_skb(ctx->skb);
                        wil_vring_advance_head(vring, 1);
                }
        }
@@ -187,7 +190,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
        d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
        d->dma.length = cpu_to_le16(sz);
        *_d = *d;
-       vring->ctx[i] = skb;
+       vring->ctx[i].skb = skb;
 
        return 0;
 }
@@ -352,11 +355,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
                return NULL;
        }
 
-       skb = vring->ctx[vring->swhead];
+       skb = vring->ctx[vring->swhead].skb;
        d = wil_skb_rxdesc(skb);
        *d = *_d;
        pa = wil_desc_addr(&d->dma.addr);
-       vring->ctx[vring->swhead] = NULL;
+       vring->ctx[vring->swhead].skb = NULL;
        wil_vring_advance_head(vring, 1);
 
        dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
@@ -407,6 +410,21 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
                return NULL;
        }
 
+       /* L4 IDENT is on when HW calculated checksum, check status
+        * and in case of error drop the packet
+        * higher stack layers will handle retransmission (if required)
+        */
+       if (d->dma.status & RX_DMA_STATUS_L4_IDENT) {
+               /* L4 protocol identified, csum calculated */
+               if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0)
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               /* If HW reports bad checksum, let IP stack re-check it
+                * For example, HW don't understand Microsoft IP stack that
+                * mis-calculates TCP checksum - if it should be 0x0,
+                * it writes 0xffff in violation of RFC 1624
+                */
+       }
+
        ds_bits = wil_rxdesc_ds_bits(d);
        if (ds_bits == 1) {
                /*
@@ -646,6 +664,53 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
        return 0;
 }
 
+static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
+                               struct vring_tx_desc *d,
+                               struct sk_buff *skb)
+{
+       int protocol;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       switch (skb->protocol) {
+       case cpu_to_be16(ETH_P_IP):
+               protocol = ip_hdr(skb)->protocol;
+               break;
+       case cpu_to_be16(ETH_P_IPV6):
+               protocol = ipv6_hdr(skb)->nexthdr;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (protocol) {
+       case IPPROTO_TCP:
+               d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS);
+               /* L4 header len: TCP header length */
+               d->dma.d0 |=
+               (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+               break;
+       case IPPROTO_UDP:
+               /* L4 header len: UDP header length */
+               d->dma.d0 |=
+               (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       d->dma.ip_length = skb_network_header_len(skb);
+       d->dma.b11 = ETH_HLEN; /* MAC header length */
+       d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);
+       /* Enable TCP/UDP checksum */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS);
+       /* Calculate pseudo-header */
+       d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS);
+
+       return 0;
+}
+
 static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                        struct sk_buff *skb)
 {
@@ -655,7 +720,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        u32 swhead = vring->swhead;
        int avail = wil_vring_avail_tx(vring);
        int nr_frags = skb_shinfo(skb)->nr_frags;
-       uint f;
+       uint f = 0;
        int vring_index = vring - wil->vring_tx;
        uint i = swhead;
        dma_addr_t pa;
@@ -686,13 +751,20 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                return -EINVAL;
        /* 1-st segment */
        wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+       /* Process TCP/UDP checksum offloading */
+       if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
+               wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
+                       vring_index);
+               goto dma_error;
+       }
+
        d->mac.d[2] |= ((nr_frags + 1) <<
                       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
        if (nr_frags)
                *_d = *d;
 
        /* middle segments */
-       for (f = 0; f < nr_frags; f++) {
+       for (; f < nr_frags; f++) {
                const struct skb_frag_struct *frag =
                                &skb_shinfo(skb)->frags[f];
                int len = skb_frag_size(frag);
@@ -703,7 +775,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
                if (unlikely(dma_mapping_error(dev, pa)))
                        goto dma_error;
                wil_tx_desc_map(d, pa, len, vring_index);
-               vring->ctx[i] = NULL;
+               vring->ctx[i].mapped_as_page = 1;
                *_d = *d;
        }
        /* for the last seg only */
@@ -712,6 +784,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
        *_d = *d;
 
+       /* hold reference to skb
+        * to prevent skb release before accounting
+        * in case of immediate "tx done"
+        */
+       vring->ctx[i].skb = skb_get(skb);
+
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
@@ -720,29 +798,31 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
        trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
        iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
-       /* hold reference to skb
-        * to prevent skb release before accounting
-        * in case of immediate "tx done"
-        */
-       vring->ctx[i] = skb_get(skb);
 
        return 0;
  dma_error:
        /* unmap what we have mapped */
-       /* Note: increment @f to operate with positive index */
-       for (f++; f > 0; f--) {
+       nr_frags = f + 1; /* frags mapped + one for skb head */
+       for (f = 0; f < nr_frags; f++) {
                u16 dmalen;
+               struct wil_ctx *ctx;
 
                i = (swhead + f) % vring->size;
+               ctx = &vring->ctx[i];
                _d = &(vring->va[i].tx);
                *d = *_d;
                _d->dma.status = TX_DMA_STATUS_DU;
                pa = wil_desc_addr(&d->dma.addr);
                dmalen = le16_to_cpu(d->dma.length);
-               if (vring->ctx[i])
-                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
-               else
+               if (ctx->mapped_as_page)
                        dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+               else
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
+               if (ctx->skb)
+                       dev_kfree_skb_any(ctx->skb);
+
+               memset(ctx, 0, sizeof(*ctx));
        }
 
        return -EINVAL;
@@ -821,8 +901,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                              &vring->va[vring->swtail].tx;
                struct vring_tx_desc dd, *d = &dd;
                dma_addr_t pa;
-               struct sk_buff *skb;
                u16 dmalen;
+               struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+               struct sk_buff *skb = ctx->skb;
 
                *d = *_d;
 
@@ -840,7 +921,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                  (const void *)d, sizeof(*d), false);
 
                pa = wil_desc_addr(&d->dma.addr);
-               skb = vring->ctx[vring->swtail];
+               if (ctx->mapped_as_page)
+                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+               else
+                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
                if (skb) {
                        if (d->dma.error == 0) {
                                ndev->stats.tx_packets++;
@@ -849,16 +934,15 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                                ndev->stats.tx_errors++;
                        }
 
-                       dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
                        dev_kfree_skb_any(skb);
-                       vring->ctx[vring->swtail] = NULL;
-               } else {
-                       dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
                }
-               d->dma.addr.addr_low = 0;
-               d->dma.addr.addr_high = 0;
-               d->dma.length = 0;
-               d->dma.status = TX_DMA_STATUS_DU;
+               memset(ctx, 0, sizeof(*ctx));
+               /*
+                * There is no need to touch HW descriptor:
+                * - ststus bit TX_DMA_STATUS_DU is set by design,
+                *   so hardware will not try to process this desc.,
+                * - rest of descriptor will be initialized on Tx.
+                */
                vring->swtail = wil_vring_next_tail(vring);
                done++;
        }
index 859aea6..b382827 100644 (file)
@@ -235,7 +235,16 @@ struct vring_tx_mac {
 
 #define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
 #define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
-#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
+#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
+
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
 
 
 #define TX_DMA_STATUS_DU         BIT(0)
@@ -334,8 +343,17 @@ struct vring_rx_mac {
 
 #define RX_DMA_D0_CMD_DMA_IT     BIT(10)
 
+/* Error field, offload bits */
+#define RX_DMA_ERROR_L3_ERR   BIT(4)
+#define RX_DMA_ERROR_L4_ERR   BIT(5)
+
+
+/* Status field */
 #define RX_DMA_STATUS_DU         BIT(0)
 #define RX_DMA_STATUS_ERROR      BIT(2)
+
+#define RX_DMA_STATUS_L3_IDENT   BIT(4)
+#define RX_DMA_STATUS_L4_IDENT   BIT(5)
 #define RX_DMA_STATUS_PHY_INFO   BIT(6)
 
 struct vring_rx_dma {
index 44fdab5..c4a5163 100644 (file)
@@ -156,11 +156,22 @@ struct wil6210_mbox_hdr {
 /* max. value for wil6210_mbox_hdr.len */
 #define MAX_MBOXITEM_SIZE   (240)
 
+/**
+ * struct wil6210_mbox_hdr_wmi - WMI header
+ *
+ * @mid: MAC ID
+ *     00 - default, created by FW
+ *     01..0f - WiFi ports, driver to create
+ *     10..fe - debug
+ *     ff - broadcast
+ * @id: command/event ID
+ * @timestamp: FW fills for events, free-running msec timer
+ */
 struct wil6210_mbox_hdr_wmi {
-       u8 reserved0[2];
+       u8 mid;
+       u8 reserved;
        __le16 id;
-       __le16 info1; /* bits [0..3] - device_id, rest - unused */
-       u8 reserved1[2];
+       __le32 timestamp;
 } __packed;
 
 struct pending_wmi_event {
@@ -172,6 +183,14 @@ struct pending_wmi_event {
        } __packed event;
 };
 
+/**
+ * struct wil_ctx - software context for Vring descriptor
+ */
+struct wil_ctx {
+       struct sk_buff *skb;
+       u8 mapped_as_page:1;
+};
+
 union vring_desc;
 
 struct vring {
@@ -181,7 +200,7 @@ struct vring {
        u32 swtail;
        u32 swhead;
        u32 hwtail; /* write here to inform hw */
-       void **ctx; /* void *ctx[size] - software context */
+       struct wil_ctx *ctx; /* ctx[size] - software context */
 };
 
 enum { /* for wil6210_priv.status */
index dc8059a..5220f15 100644 (file)
@@ -172,8 +172,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
                        .len = cpu_to_le16(sizeof(cmd.wmi) + len),
                },
                .wmi = {
+                       .mid = 0,
                        .id = cpu_to_le16(cmdid),
-                       .info1 = 0,
                },
        };
        struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
@@ -248,7 +248,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
                  offsetof(struct wil6210_mbox_ctl, tx.head));
 
-       trace_wil6210_wmi_cmd(cmdid, buf, len);
+       trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
 
        /* interrupt to FW */
        iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -640,9 +640,13 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                            hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
-                       u16 id = le16_to_cpu(evt->event.wmi.id);
-                       wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
-                       trace_wil6210_wmi_event(id, &evt->event.wmi, len);
+                       struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
+                       u16 id = le16_to_cpu(wmi->id);
+                       u32 tstamp = le32_to_cpu(wmi->timestamp);
+                       wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
+                                   id, wmi->mid, tstamp);
+                       trace_wil6210_wmi_event(wmi, &wmi[1],
+                                               len - sizeof(*wmi));
                }
                wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
                                 &evt->event.hdr, sizeof(hdr) + len, true);
@@ -920,6 +924,12 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
                cmd.sniffer_cfg.phy_support =
                        cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
                                    ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+       } else {
+               /* Initialize offload (in non-sniffer mode).
+                * Linux IP stack always calculates IP checksum
+                * HW always calculate TCP/UDP checksum
+                */
+               cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
        }
        /* typical time for secure PCP is 840ms */
        rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
index 0e933bb..ccd24f0 100644 (file)
@@ -4645,6 +4645,19 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
        b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN,
                      B43_MACCTL_PSM_JMP0);
 
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               bcma_core_pci_down(dev->dev->bdev->bus);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               /* TODO */
+               break;
+#endif
+       }
+
        b43_dma_free(dev);
        b43_pio_free(dev);
        b43_chip_exit(dev);
@@ -4684,6 +4697,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        case B43_BUS_BCMA:
                bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0],
                                      dev->dev->bdev, true);
+               bcma_core_pci_up(dev->dev->bdev->bus);
                break;
 #endif
 #ifdef CONFIG_B43_SSB
index e3f3c48..e13b1a6 100644 (file)
@@ -592,6 +592,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, u8 *buf, uint nbytes)
 {
        struct sk_buff *mypkt;
+       struct sk_buff_head pktq;
        int err;
 
        mypkt = brcmu_pkt_buf_get_skb(nbytes);
@@ -602,7 +603,10 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        }
 
        memcpy(mypkt->data, buf, nbytes);
-       err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt);
+       __skb_queue_head_init(&pktq);
+       __skb_queue_tail(&pktq, mypkt);
+       err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq);
+       __skb_dequeue_tail(&pktq);
 
        brcmu_pkt_buf_free_skb(mypkt);
        return err;
@@ -611,22 +615,18 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 
 int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff *pkt)
+                     uint flags, struct sk_buff_head *pktq)
 {
        uint width;
        int err = 0;
-       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
-                 fn, addr, pkt->len);
+                 fn, addr, pktq->qlen);
 
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
        brcmf_sdio_addrprep(sdiodev, width, &addr);
 
-       skb_queue_head_init(&pkt_list);
-       skb_queue_tail(&pkt_list, pkt);
-       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list);
-       skb_dequeue_tail(&pkt_list);
+       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq);
 
        return err;
 }
index 289e386..64f4a2b 100644 (file)
@@ -350,7 +350,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
 
        sdiodev->bus_if = bus_if;
        bus_if->bus_priv.sdio = sdiodev;
-       bus_if->align = BRCMF_SDALIGN;
        dev_set_drvdata(&func->dev, bus_if);
        dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
        sdiodev->dev = &sdiodev->func[1]->dev;
index 86cbfe2..2eb9e64 100644 (file)
 #define BRCMF_E_IF_DEL                         2
 #define BRCMF_E_IF_CHANGE                      3
 
+#define BRCMF_E_IF_FLAG_NOIF                   1
+
 #define BRCMF_E_IF_ROLE_STA                    0
 #define BRCMF_E_IF_ROLE_AP                     1
 #define BRCMF_E_IF_ROLE_WDS                    2
 #define BRCMF_DCMD_MEDLEN      1536
 #define BRCMF_DCMD_MAXLEN      8192
 
+#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS                256
+
 /* Pattern matching filter. Specifies an offset within received packets to
  * start matching, the pattern to match, the size of the pattern, and a bitmask
  * that indicates which bits within the pattern should be matched.
@@ -505,6 +509,25 @@ struct brcmf_dcmd {
        uint needed;            /* bytes needed (optional) */
 };
 
+/**
+ * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+ *
+ * @pktslots: dynamic allocated array for ordering AMPDU packets.
+ * @flow_id: AMPDU flow identifier.
+ * @cur_idx: last AMPDU index from firmware.
+ * @exp_idx: expected next AMPDU index.
+ * @max_idx: maximum amount of packets per AMPDU.
+ * @pend_pkts: number of packets currently in @pktslots.
+ */
+struct brcmf_ampdu_rx_reorder {
+       struct sk_buff **pktslots;
+       u8 flow_id;
+       u8 cur_idx;
+       u8 exp_idx;
+       u8 max_idx;
+       u8 pend_pkts;
+};
+
 /* Forward decls for struct brcmf_pub (see below) */
 struct brcmf_proto;    /* device communication protocol info */
 struct brcmf_cfg80211_dev; /* cfg80211 device info */
@@ -536,9 +559,10 @@ struct brcmf_pub {
 
        struct brcmf_fweh_info fweh;
 
-       bool fw_signals;
        struct brcmf_fws_info *fws;
-       spinlock_t fws_spinlock;
+
+       struct brcmf_ampdu_rx_reorder
+               *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
 #ifdef DEBUG
        struct dentry *dbgfs_dir;
 #endif
@@ -604,6 +628,9 @@ struct brcmf_if {
        wait_queue_head_t pend_8021x_wait;
 };
 
+struct brcmf_skb_reorder_data {
+       u8 *reorder;
+};
 
 extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
 
index 080395f..f7c1985 100644 (file)
@@ -36,7 +36,11 @@ struct brcmf_bus_dcmd {
  *
  * @init: prepare for communication with dongle.
  * @stop: clear pending frames, disable data flow.
- * @txdata: send a data frame to the dongle (callee disposes skb).
+ * @txdata: send a data frame to the dongle. When the data
+ *     has been transferred, the common driver must be
+ *     notified using brcmf_txcomplete(). The common
+ *     driver calls this function with interrupts
+ *     disabled.
  * @txctl: transmit a control request message to dongle.
  * @rxctl: receive a control response message from dongle.
  * @gettxq: obtain a reference of bus transmit queue (optional).
@@ -65,7 +69,6 @@ struct brcmf_bus_ops {
  * @maxctl: maximum size for rxctl request message.
  * @tx_realloc: number of tx packets realloced for headroom.
  * @dstats: dongle-based statistical data.
- * @align: alignment requirement for the bus.
  * @dcmd_list: bus/device specific dongle initialization commands.
  * @chip: device identifier of the dongle chip.
  * @chiprev: revision of the dongle chip.
@@ -80,7 +83,6 @@ struct brcmf_bus {
        enum brcmf_bus_state state;
        uint maxctl;
        unsigned long tx_realloc;
-       u8 align;
        u32 chip;
        u32 chiprev;
        struct list_head dcmd_list;
index 8009901..e067aec 100644 (file)
@@ -38,6 +38,19 @@ MODULE_LICENSE("Dual BSD/GPL");
 
 #define MAX_WAIT_FOR_8021X_TX          50      /* msecs */
 
+/* AMPDU rx reordering definitions */
+#define BRCMF_RXREORDER_FLOWID_OFFSET          0
+#define BRCMF_RXREORDER_MAXIDX_OFFSET          2
+#define BRCMF_RXREORDER_FLAGS_OFFSET           4
+#define BRCMF_RXREORDER_CURIDX_OFFSET          6
+#define BRCMF_RXREORDER_EXPIDX_OFFSET          8
+
+#define BRCMF_RXREORDER_DEL_FLOW               0x01
+#define BRCMF_RXREORDER_FLUSH_ALL              0x02
+#define BRCMF_RXREORDER_CURIDX_VALID           0x04
+#define BRCMF_RXREORDER_EXPIDX_VALID           0x08
+#define BRCMF_RXREORDER_NEW_HOLE               0x10
+
 /* Error bits */
 int brcmf_msg_level;
 module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
@@ -265,17 +278,234 @@ void brcmf_txflowblock(struct device *dev, bool state)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
-       int i;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       if (brcmf_fws_fc_active(drvr->fws)) {
-               brcmf_fws_bus_blocked(drvr, state);
+       brcmf_fws_bus_blocked(drvr, state);
+}
+
+static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+       skb->dev = ifp->ndev;
+       skb->protocol = eth_type_trans(skb, skb->dev);
+
+       if (skb->pkt_type == PACKET_MULTICAST)
+               ifp->stats.multicast++;
+
+       /* Process special event packets */
+       brcmf_fweh_process_skb(ifp->drvr, skb);
+
+       if (!(ifp->ndev->flags & IFF_UP)) {
+               brcmu_pkt_buf_free_skb(skb);
+               return;
+       }
+
+       ifp->stats.rx_bytes += skb->len;
+       ifp->stats.rx_packets++;
+
+       brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
+       if (in_interrupt())
+               netif_rx(skb);
+       else
+               /* If the receive is not processed inside an ISR,
+                * the softirqd must be woken explicitly to service
+                * the NET_RX_SOFTIRQ.  This is handled by netif_rx_ni().
+                */
+               netif_rx_ni(skb);
+}
+
+static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+                                        u8 start, u8 end,
+                                        struct sk_buff_head *skb_list)
+{
+       /* initialize return list */
+       __skb_queue_head_init(skb_list);
+
+       if (rfi->pend_pkts == 0) {
+               brcmf_dbg(INFO, "no packets in reorder queue\n");
+               return;
+       }
+
+       do {
+               if (rfi->pktslots[start]) {
+                       __skb_queue_tail(skb_list, rfi->pktslots[start]);
+                       rfi->pktslots[start] = NULL;
+               }
+               start++;
+               if (start > rfi->max_idx)
+                       start = 0;
+       } while (start != end);
+       rfi->pend_pkts -= skb_queue_len(skb_list);
+}
+
+static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
+                                        struct sk_buff *pkt)
+{
+       u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+       struct brcmf_ampdu_rx_reorder *rfi;
+       struct sk_buff_head reorder_list;
+       struct sk_buff *pnext;
+       u8 flags;
+       u32 buf_size;
+
+       flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+       flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+
+       /* validate flags and flow id */
+       if (flags == 0xFF) {
+               brcmf_err("invalid flags...so ignore this packet\n");
+               brcmf_netif_rx(ifp, pkt);
+               return;
+       }
+
+       rfi = ifp->drvr->reorder_flows[flow_id];
+       if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+               brcmf_dbg(INFO, "flow-%d: delete\n",
+                         flow_id);
+
+               if (rfi == NULL) {
+                       brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+                                 flow_id);
+                       brcmf_netif_rx(ifp, pkt);
+                       return;
+               }
+
+               brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+                                            &reorder_list);
+               /* add the last packet */
+               __skb_queue_tail(&reorder_list, pkt);
+               kfree(rfi);
+               ifp->drvr->reorder_flows[flow_id] = NULL;
+               goto netif_rx;
+       }
+       /* from here on we need a flow reorder instance */
+       if (rfi == NULL) {
+               buf_size = sizeof(*rfi);
+               max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+
+               buf_size += (max_idx + 1) * sizeof(pkt);
+
+               /* allocate space for flow reorder info */
+               brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+                         flow_id, max_idx);
+               rfi = kzalloc(buf_size, GFP_ATOMIC);
+               if (rfi == NULL) {
+                       brcmf_err("failed to alloc buffer\n");
+                       brcmf_netif_rx(ifp, pkt);
+                       return;
+               }
+
+               ifp->drvr->reorder_flows[flow_id] = rfi;
+               rfi->pktslots = (struct sk_buff **)(rfi+1);
+               rfi->max_idx = max_idx;
+       }
+       if (flags & BRCMF_RXREORDER_NEW_HOLE)  {
+               if (rfi->pend_pkts) {
+                       brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+                                                    rfi->exp_idx,
+                                                    &reorder_list);
+                       WARN_ON(rfi->pend_pkts);
+               } else {
+                       __skb_queue_head_init(&reorder_list);
+               }
+               rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+               rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+               rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+               rfi->pktslots[rfi->cur_idx] = pkt;
+               rfi->pend_pkts++;
+               brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+                         flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+       } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+               cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+               exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+               if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+                       /* still in the current hole */
+                       /* enqueue the current on the buffer chain */
+                       if (rfi->pktslots[cur_idx] != NULL) {
+                               brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+                               brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+                               rfi->pktslots[cur_idx] = NULL;
+                       }
+                       rfi->pktslots[cur_idx] = pkt;
+                       rfi->pend_pkts++;
+                       rfi->cur_idx = cur_idx;
+                       brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+                                 flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+                       /* can return now as there is no reorder
+                        * list to process.
+                        */
+                       return;
+               }
+               if (rfi->exp_idx == cur_idx) {
+                       if (rfi->pktslots[cur_idx] != NULL) {
+                               brcmf_dbg(INFO, "error buffer pending..free it\n");
+                               brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+                               rfi->pktslots[cur_idx] = NULL;
+                       }
+                       rfi->pktslots[cur_idx] = pkt;
+                       rfi->pend_pkts++;
+
+                       /* got the expected one. flush from current to expected
+                        * and update expected
+                        */
+                       brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+                                 flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+                       rfi->cur_idx = cur_idx;
+                       rfi->exp_idx = exp_idx;
+
+                       brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+                                                    &reorder_list);
+                       brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+                                 flow_id, skb_queue_len(&reorder_list),
+                                 rfi->pend_pkts);
+               } else {
+                       u8 end_idx;
+
+                       brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+                                 flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+                                 cur_idx, exp_idx);
+                       if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+                               end_idx = rfi->exp_idx;
+                       else
+                               end_idx = exp_idx;
+
+                       /* flush pkts first */
+                       brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+                                                    &reorder_list);
+
+                       if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+                               __skb_queue_tail(&reorder_list, pkt);
+                       } else {
+                               rfi->pktslots[cur_idx] = pkt;
+                               rfi->pend_pkts++;
+                       }
+                       rfi->exp_idx = exp_idx;
+                       rfi->cur_idx = cur_idx;
+               }
        } else {
-               for (i = 0; i < BRCMF_MAX_IFS; i++)
-                       brcmf_txflowblock_if(drvr->iflist[i],
-                                            BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
-                                            state);
+               /* explicity window move updating the expected index */
+               exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+               brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+                         flow_id, flags, rfi->exp_idx, exp_idx);
+               if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+                       end_idx =  rfi->exp_idx;
+               else
+                       end_idx =  exp_idx;
+
+               brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+                                            &reorder_list);
+               __skb_queue_tail(&reorder_list, pkt);
+               /* set the new expected idx */
+               rfi->exp_idx = exp_idx;
+       }
+netif_rx:
+       skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+               __skb_unlink(pkt, &reorder_list);
+               brcmf_netif_rx(ifp, pkt);
        }
 }
 
@@ -285,16 +515,18 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        struct brcmf_if *ifp;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
+       struct brcmf_skb_reorder_data *rd;
        u8 ifidx;
        int ret;
 
-       brcmf_dbg(DATA, "Enter\n");
+       brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev),
+                 skb_queue_len(skb_list));
 
        skb_queue_walk_safe(skb_list, skb, pnext) {
                skb_unlink(skb, skb_list);
 
                /* process and remove protocol-specific header */
-               ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
+               ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
                ifp = drvr->iflist[ifidx];
 
                if (ret || !ifp || !ifp->ndev) {
@@ -304,31 +536,11 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
                        continue;
                }
 
-               skb->dev = ifp->ndev;
-               skb->protocol = eth_type_trans(skb, skb->dev);
-
-               if (skb->pkt_type == PACKET_MULTICAST)
-                       ifp->stats.multicast++;
-
-               /* Process special event packets */
-               brcmf_fweh_process_skb(drvr, skb);
-
-               if (!(ifp->ndev->flags & IFF_UP)) {
-                       brcmu_pkt_buf_free_skb(skb);
-                       continue;
-               }
-
-               ifp->stats.rx_bytes += skb->len;
-               ifp->stats.rx_packets++;
-
-               if (in_interrupt())
-                       netif_rx(skb);
+               rd = (struct brcmf_skb_reorder_data *)skb->cb;
+               if (rd->reorder)
+                       brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
                else
-                       /* If the receive is not processed inside an ISR,
-                        * the softirqd must be woken explicitly to service the
-                        * NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
-                        */
-                       netif_rx_ni(skb);
+                       brcmf_netif_rx(ifp, skb);
        }
 }
 
@@ -889,7 +1101,6 @@ int brcmf_bus_start(struct device *dev)
        if (ret < 0)
                goto fail;
 
-       drvr->fw_signals = true;
        ret = brcmf_fws_init(drvr);
        if (ret < 0)
                goto fail;
index 2641119..1aa75d5 100644 (file)
@@ -201,13 +201,6 @@ struct rte_console {
 #define SFC_CRC4WOOS   (1 << 2)        /* CRC error for write out of sync */
 #define SFC_ABORTALL   (1 << 3)        /* Abort all in-progress frames */
 
-/* HW frame tag */
-#define SDPCM_FRAMETAG_LEN     4       /* 2 bytes len, 2 bytes check val */
-
-/* Total length of frame header for dongle protocol */
-#define SDPCM_HDRLEN   (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
-#define SDPCM_RESERVE  (SDPCM_HDRLEN + BRCMF_SDALIGN)
-
 /*
  * Software allocation of To SB Mailbox resources
  */
@@ -250,38 +243,6 @@ struct rte_console {
 /* Current protocol version */
 #define SDPCM_PROT_VERSION     4
 
-/* SW frame header */
-#define SDPCM_PACKET_SEQUENCE(p)       (((u8 *)p)[0] & 0xff)
-
-#define SDPCM_CHANNEL_MASK             0x00000f00
-#define SDPCM_CHANNEL_SHIFT            8
-#define SDPCM_PACKET_CHANNEL(p)                (((u8 *)p)[1] & 0x0f)
-
-#define SDPCM_NEXTLEN_OFFSET           2
-
-/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
-#define SDPCM_DOFFSET_OFFSET           3       /* Data Offset */
-#define SDPCM_DOFFSET_VALUE(p)         (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
-#define SDPCM_DOFFSET_MASK             0xff000000
-#define SDPCM_DOFFSET_SHIFT            24
-#define SDPCM_FCMASK_OFFSET            4       /* Flow control */
-#define SDPCM_FCMASK_VALUE(p)          (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
-#define SDPCM_WINDOW_OFFSET            5       /* Credit based fc */
-#define SDPCM_WINDOW_VALUE(p)          (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
-
-#define SDPCM_SWHEADER_LEN     8       /* SW header is 64 bits */
-
-/* logical channel numbers */
-#define SDPCM_CONTROL_CHANNEL  0       /* Control channel Id */
-#define SDPCM_EVENT_CHANNEL    1       /* Asyc Event Indication Channel Id */
-#define SDPCM_DATA_CHANNEL     2       /* Data Xmit/Recv Channel Id */
-#define SDPCM_GLOM_CHANNEL     3       /* For coalesced packets */
-#define SDPCM_TEST_CHANNEL     15      /* Reserved for test/debug packets */
-
-#define SDPCM_SEQUENCE_WRAP    256     /* wrap-around val for 8bit frame seq */
-
-#define SDPCM_GLOMDESC(p)      (((u8 *)p)[1] & 0x80)
-
 /*
  * Shared structure between dongle and the host.
  * The structure contains pointers to trap or assert information.
@@ -396,8 +357,8 @@ struct sdpcm_shared_le {
        __le32 brpt_addr;
 };
 
-/* SDIO read frame info */
-struct brcmf_sdio_read {
+/* dongle SDIO bus specific header info */
+struct brcmf_sdio_hdrinfo {
        u8 seq_num;
        u8 channel;
        u16 len;
@@ -431,7 +392,7 @@ struct brcmf_sdio {
        u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
        u8 *rxhdr;              /* Header of current rx frame (in hdrbuf) */
        u8 rx_seq;              /* Receive sequence number (expected) */
-       struct brcmf_sdio_read cur_read;
+       struct brcmf_sdio_hdrinfo cur_read;
                                /* info of current read frame */
        bool rxskip;            /* Skip receive (awaiting NAK ACK) */
        bool rxpending;         /* Data frame pending in dongle */
@@ -500,6 +461,8 @@ struct brcmf_sdio {
        struct brcmf_sdio_count sdcnt;
        bool sr_enabled; /* SaveRestore enabled */
        bool sleeping; /* SDIO bus sleeping */
+
+       u8 tx_hdrlen;           /* sdio bus header length for tx packet */
 };
 
 /* clkstate */
@@ -510,7 +473,6 @@ struct brcmf_sdio {
 
 #ifdef DEBUG
 static int qcount[NUMPRIO];
-static int tx_packets[NUMPRIO];
 #endif                         /* DEBUG */
 
 #define DEFAULT_SDIO_DRIVE_STRENGTH    6       /* in milliamps */
@@ -1043,18 +1005,63 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
        }
 }
 
-static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
-                              struct brcmf_sdio_read *rd,
-                              enum brcmf_sdio_frmtype type)
+/**
+ * brcmfmac sdio bus specific header
+ * This is the lowest layer header wrapped on the packets transmitted between
+ * host and WiFi dongle which contains information needed for SDIO core and
+ * firmware
+ *
+ * It consists of 2 parts: hw header and software header
+ * hardware header (frame tag) - 4 bytes
+ * Byte 0~1: Frame length
+ * Byte 2~3: Checksum, bit-wise inverse of frame length
+ * software header - 8 bytes
+ * Byte 0: Rx/Tx sequence number
+ * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+ * Byte 2: Length of next data frame, reserved for Tx
+ * Byte 3: Data offset
+ * Byte 4: Flow control bits, reserved for Tx
+ * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
+ * Byte 6~7: Reserved
+ */
+#define SDPCM_HWHDR_LEN                        4
+#define SDPCM_SWHDR_LEN                        8
+#define SDPCM_HDRLEN                   (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
+/* software header */
+#define SDPCM_SEQ_MASK                 0x000000ff
+#define SDPCM_SEQ_WRAP                 256
+#define SDPCM_CHANNEL_MASK             0x00000f00
+#define SDPCM_CHANNEL_SHIFT            8
+#define SDPCM_CONTROL_CHANNEL          0       /* Control */
+#define SDPCM_EVENT_CHANNEL            1       /* Asyc Event Indication */
+#define SDPCM_DATA_CHANNEL             2       /* Data Xmit/Recv */
+#define SDPCM_GLOM_CHANNEL             3       /* Coalesced packets */
+#define SDPCM_TEST_CHANNEL             15      /* Test/debug packets */
+#define SDPCM_GLOMDESC(p)              (((u8 *)p)[1] & 0x80)
+#define SDPCM_NEXTLEN_MASK             0x00ff0000
+#define SDPCM_NEXTLEN_SHIFT            16
+#define SDPCM_DOFFSET_MASK             0xff000000
+#define SDPCM_DOFFSET_SHIFT            24
+#define SDPCM_FCMASK_MASK              0x000000ff
+#define SDPCM_WINDOW_MASK              0x0000ff00
+#define SDPCM_WINDOW_SHIFT             8
+
+static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
+{
+       u32 hdrvalue;
+       hdrvalue = *(u32 *)swheader;
+       return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+}
+
+static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+                             struct brcmf_sdio_hdrinfo *rd,
+                             enum brcmf_sdio_frmtype type)
 {
        u16 len, checksum;
        u8 rx_seq, fc, tx_seq_max;
+       u32 swheader;
 
-       /*
-        * 4 bytes hardware header (frame tag)
-        * Byte 0~1: Frame length
-        * Byte 2~3: Checksum, bit-wise inverse of frame length
-        */
+       /* hw header */
        len = get_unaligned_le16(header);
        checksum = get_unaligned_le16(header + sizeof(u16));
        /* All zero means no more to read */
@@ -1083,24 +1090,16 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
        }
        rd->len = len;
 
-       /*
-        * 8 bytes hardware header
-        * Byte 0: Rx sequence number
-        * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
-        * Byte 2: Length of next data frame
-        * Byte 3: Data offset
-        * Byte 4: Flow control bits
-        * Byte 5: Maximum Sequence number allow for Tx
-        * Byte 6~7: Reserved
-        */
-       if (type == BRCMF_SDIO_FT_SUPER &&
-           SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) {
+       /* software header */
+       header += SDPCM_HWHDR_LEN;
+       swheader = le32_to_cpu(*(__le32 *)header);
+       if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
                brcmf_err("Glom descriptor found in superframe head\n");
                rd->len = 0;
                return -EINVAL;
        }
-       rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]);
-       rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]);
+       rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
+       rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
        if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
            type != BRCMF_SDIO_FT_SUPER) {
                brcmf_err("HW header length too long\n");
@@ -1120,7 +1119,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
                rd->len = 0;
                return -EINVAL;
        }
-       rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+       rd->dat_offset = brcmf_sdio_getdatoffset(header);
        if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
                brcmf_err("seq %d: bad data offset\n", rx_seq);
                bus->sdcnt.rx_badhdr++;
@@ -1137,14 +1136,15 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
        /* no need to check the reset for subframe */
        if (type == BRCMF_SDIO_FT_SUB)
                return 0;
-       rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+       rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
        if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
                /* only warm for NON glom packet */
                if (rd->channel != SDPCM_GLOM_CHANNEL)
                        brcmf_err("seq %d: next length error\n", rx_seq);
                rd->len_nxtfrm = 0;
        }
-       fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+       swheader = le32_to_cpu(*(__le32 *)(header + 4));
+       fc = swheader & SDPCM_FCMASK_MASK;
        if (bus->flowcontrol != fc) {
                if (~bus->flowcontrol & fc)
                        bus->sdcnt.fc_xoff++;
@@ -1153,7 +1153,7 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
                bus->sdcnt.fc_rcvd++;
                bus->flowcontrol = fc;
        }
-       tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+       tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
        if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
                brcmf_err("seq %d: max tx seq number error\n", rx_seq);
                tx_seq_max = bus->tx_seq + 2;
@@ -1163,18 +1163,40 @@ static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
        return 0;
 }
 
+static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
+{
+       *(__le16 *)header = cpu_to_le16(frm_length);
+       *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
+}
+
+static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
+                             struct brcmf_sdio_hdrinfo *hd_info)
+{
+       u32 sw_header;
+
+       brcmf_sdio_update_hwhdr(header, hd_info->len);
+
+       sw_header = bus->tx_seq;
+       sw_header |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
+                    SDPCM_CHANNEL_MASK;
+       sw_header |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
+                    SDPCM_DOFFSET_MASK;
+       *(((__le32 *)header) + 1) = cpu_to_le32(sw_header);
+       *(((__le32 *)header) + 2) = 0;
+}
+
 static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
 {
        u16 dlen, totlen;
        u8 *dptr, num = 0;
-
+       u32 align = 0;
        u16 sublen;
        struct sk_buff *pfirst, *pnext;
 
        int errcode;
        u8 doff, sfdoff;
 
-       struct brcmf_sdio_read rd_new;
+       struct brcmf_sdio_hdrinfo rd_new;
 
        /* If packets, issue read(s) and send up packet chain */
        /* Return sequence numbers consumed? */
@@ -1182,6 +1204,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
                  bus->glomd, skb_peek(&bus->glom));
 
+       if (bus->sdiodev->pdata)
+               align = bus->sdiodev->pdata->sd_sgentry_align;
+       if (align < 4)
+               align = 4;
+
        /* If there's a descriptor, generate the packet chain */
        if (bus->glomd) {
                pfirst = pnext = NULL;
@@ -1205,9 +1232,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                                pnext = NULL;
                                break;
                        }
-                       if (sublen % BRCMF_SDALIGN) {
+                       if (sublen % align) {
                                brcmf_err("sublen %d not multiple of %d\n",
-                                         sublen, BRCMF_SDALIGN);
+                                         sublen, align);
                        }
                        totlen += sublen;
 
@@ -1220,7 +1247,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        }
 
                        /* Allocate/chain packet for next subframe */
-                       pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
+                       pnext = brcmu_pkt_buf_get_skb(sublen + align);
                        if (pnext == NULL) {
                                brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
                                          num, sublen);
@@ -1229,7 +1256,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        skb_queue_tail(&bus->glom, pnext);
 
                        /* Adhere to start alignment requirements */
-                       pkt_align(pnext, sublen, BRCMF_SDALIGN);
+                       pkt_align(pnext, sublen, align);
                }
 
                /* If all allocations succeeded, save packet chain
@@ -1305,8 +1332,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                rd_new.seq_num = rxseq;
                rd_new.len = dlen;
                sdio_claim_host(bus->sdiodev->func[1]);
-               errcode = brcmf_sdio_hdparser(bus, pfirst->data, &rd_new,
-                                             BRCMF_SDIO_FT_SUPER);
+               errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
+                                            BRCMF_SDIO_FT_SUPER);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->cur_read.len = rd_new.len_nxtfrm << 4;
 
@@ -1324,8 +1351,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        rd_new.len = pnext->len;
                        rd_new.seq_num = rxseq++;
                        sdio_claim_host(bus->sdiodev->func[1]);
-                       errcode = brcmf_sdio_hdparser(bus, pnext->data, &rd_new,
-                                                     BRCMF_SDIO_FT_SUB);
+                       errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
+                                                    BRCMF_SDIO_FT_SUB);
                        sdio_release_host(bus->sdiodev->func[1]);
                        brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
                                           pnext->data, 32, "subframe:\n");
@@ -1357,7 +1384,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
                        dptr = (u8 *) (pfirst->data);
                        sublen = get_unaligned_le16(dptr);
-                       doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+                       doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
 
                        brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
                                           dptr, pfirst->len,
@@ -1535,7 +1562,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
        uint rxleft = 0;        /* Remaining number of frames allowed */
        int ret;                /* Return code from calls */
        uint rxcount = 0;       /* Total frames read */
-       struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
+       struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
        u8 head_read = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -1583,8 +1610,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                                           bus->rxhdr, SDPCM_HDRLEN,
                                           "RxHdr:\n");
 
-                       if (brcmf_sdio_hdparser(bus, bus->rxhdr, rd,
-                                               BRCMF_SDIO_FT_NORMAL)) {
+                       if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
+                                              BRCMF_SDIO_FT_NORMAL)) {
                                sdio_release_host(bus->sdiodev->func[1]);
                                if (!bus->rxpending)
                                        break;
@@ -1648,8 +1675,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                        memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
                        rd_new.seq_num = rd->seq_num;
                        sdio_claim_host(bus->sdiodev->func[1]);
-                       if (brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new,
-                                               BRCMF_SDIO_FT_NORMAL)) {
+                       if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
+                                              BRCMF_SDIO_FT_NORMAL)) {
                                rd->len = 0;
                                brcmu_pkt_buf_free_skb(pkt);
                        }
@@ -1693,7 +1720,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 
                /* Save superframe descriptor and allocate packet frame */
                if (rd->channel == SDPCM_GLOM_CHANNEL) {
-                       if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+                       if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
                                brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
                                          rd->len);
                                brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
@@ -1759,85 +1786,168 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
        return;
 }
 
+/* flag marking a dummy skb added for DMA alignment requirement */
+#define DUMMY_SKB_FLAG         0x10000
+/* bit mask of data length chopped from the previous packet */
+#define DUMMY_SKB_CHOP_LEN_MASK        0xffff
+/**
+ * brcmf_sdio_txpkt_prep - packet preparation for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ * @chan: virtual channel to transmit the packet
+ *
+ * Processes to be applied to the packet
+ *     - Align data buffer pointer
+ *     - Align data buffer length
+ *     - Prepare header
+ * Return: negative value if there is error
+ */
+static int
+brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+                     uint chan)
+{
+       u16 head_pad, tail_pad, tail_chop, head_align, sg_align;
+       int ntail;
+       struct sk_buff *pkt_next, *pkt_new;
+       u8 *dat_buf;
+       unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize;
+       struct brcmf_sdio_hdrinfo hd_info = {0};
+
+       /* SDIO ADMA requires at least 32 bit alignment */
+       head_align = 4;
+       sg_align = 4;
+       if (bus->sdiodev->pdata) {
+               head_align = bus->sdiodev->pdata->sd_head_align > 4 ?
+                            bus->sdiodev->pdata->sd_head_align : 4;
+               sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ?
+                          bus->sdiodev->pdata->sd_sgentry_align : 4;
+       }
+       /* sg entry alignment should be a divisor of block size */
+       WARN_ON(blksize % sg_align);
+
+       pkt_next = pktq->next;
+       dat_buf = (u8 *)(pkt_next->data);
+
+       /* Check head padding */
+       head_pad = ((unsigned long)dat_buf % head_align);
+       if (head_pad) {
+               if (skb_headroom(pkt_next) < head_pad) {
+                       bus->sdiodev->bus_if->tx_realloc++;
+                       head_pad = 0;
+                       if (skb_cow(pkt_next, head_pad))
+                               return -ENOMEM;
+               }
+               skb_push(pkt_next, head_pad);
+               dat_buf = (u8 *)(pkt_next->data);
+               memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
+       }
+
+       /* Check tail padding */
+       pkt_new = NULL;
+       tail_chop = pkt_next->len % sg_align;
+       tail_pad = sg_align - tail_chop;
+       tail_pad += blksize - (pkt_next->len + tail_pad) % blksize;
+       if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) {
+               pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop);
+               if (pkt_new == NULL)
+                       return -ENOMEM;
+               memcpy(pkt_new->data,
+                      pkt_next->data + pkt_next->len - tail_chop,
+                      tail_chop);
+               *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop;
+               skb_trim(pkt_next, pkt_next->len - tail_chop);
+               __skb_queue_after(pktq, pkt_next, pkt_new);
+       } else {
+               ntail = pkt_next->data_len + tail_pad -
+                       (pkt_next->end - pkt_next->tail);
+               if (skb_cloned(pkt_next) || ntail > 0)
+                       if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC))
+                               return -ENOMEM;
+               if (skb_linearize(pkt_next))
+                       return -ENOMEM;
+               dat_buf = (u8 *)(pkt_next->data);
+               __skb_put(pkt_next, tail_pad);
+       }
+
+       /* Now prep the header */
+       if (pkt_new)
+               hd_info.len = pkt_next->len + tail_chop;
+       else
+               hd_info.len = pkt_next->len - tail_pad;
+       hd_info.channel = chan;
+       hd_info.dat_offset = head_pad + bus->tx_hdrlen;
+       brcmf_sdio_hdpack(bus, dat_buf, &hd_info);
+
+       if (BRCMF_BYTES_ON() &&
+           ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
+            (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
+               brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n");
+       else if (BRCMF_HDRS_ON())
+               brcmf_dbg_hex_dump(true, pkt_next, head_pad + bus->tx_hdrlen,
+                                  "Tx Header:\n");
+
+       return 0;
+}
+
+/**
+ * brcmf_sdio_txpkt_postp - packet post processing for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ *
+ * Processes to be applied to the packet
+ *     - Remove head padding
+ *     - Remove tail padding
+ */
+static void
+brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
+{
+       u8 *hdr;
+       u32 dat_offset;
+       u32 dummy_flags, chop_len;
+       struct sk_buff *pkt_next, *tmp, *pkt_prev;
+
+       skb_queue_walk_safe(pktq, pkt_next, tmp) {
+               dummy_flags = *(u32 *)(pkt_next->cb);
+               if (dummy_flags & DUMMY_SKB_FLAG) {
+                       chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK;
+                       if (chop_len) {
+                               pkt_prev = pkt_next->prev;
+                               memcpy(pkt_prev->data + pkt_prev->len,
+                                      pkt_next->data, chop_len);
+                               skb_put(pkt_prev, chop_len);
+                       }
+                       __skb_unlink(pkt_next, pktq);
+                       brcmu_pkt_buf_free_skb(pkt_next);
+               } else {
+                       hdr = pkt_next->data + SDPCM_HWHDR_LEN;
+                       dat_offset = le32_to_cpu(*(__le32 *)hdr);
+                       dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
+                                    SDPCM_DOFFSET_SHIFT;
+                       skb_pull(pkt_next, dat_offset);
+               }
+       }
+}
+
 /* Writes a HW/SW header into the packet and sends it. */
 /* Assumes: (a) header space already there, (b) caller holds lock */
 static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
                              uint chan)
 {
        int ret;
-       u8 *frame;
-       u16 len, pad = 0;
-       u32 swheader;
        int i;
+       struct sk_buff_head localq;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       frame = (u8 *) (pkt->data);
-
-       /* Add alignment padding, allocate new packet if needed */
-       pad = ((unsigned long)frame % BRCMF_SDALIGN);
-       if (pad) {
-               if (skb_headroom(pkt) < pad) {
-                       brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
-                                 skb_headroom(pkt), pad);
-                       bus->sdiodev->bus_if->tx_realloc++;
-                       ret = skb_cow(pkt, BRCMF_SDALIGN);
-                       if (ret)
-                               goto done;
-                       pad = ((unsigned long)frame % BRCMF_SDALIGN);
-               }
-               skb_push(pkt, pad);
-               frame = (u8 *) (pkt->data);
-               memset(frame, 0, pad + SDPCM_HDRLEN);
-       }
-       /* precondition: pad < BRCMF_SDALIGN */
-
-       /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
-       len = (u16) (pkt->len);
-       *(__le16 *) frame = cpu_to_le16(len);
-       *(((__le16 *) frame) + 1) = cpu_to_le16(~len);
-
-       /* Software tag: channel, sequence number, data offset */
-       swheader =
-           ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
-           (((pad +
-              SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
-
-       *(((__le32 *) frame) + 1) = cpu_to_le32(swheader);
-       *(((__le32 *) frame) + 2) = 0;
-
-#ifdef DEBUG
-       tx_packets[pkt->priority]++;
-#endif
-
-       brcmf_dbg_hex_dump(BRCMF_BYTES_ON() &&
-                          ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
-                           (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)),
-                          frame, len, "Tx Frame:\n");
-       brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
-                            ((BRCMF_CTL_ON() &&
-                              chan == SDPCM_CONTROL_CHANNEL) ||
-                             (BRCMF_DATA_ON() &&
-                              chan != SDPCM_CONTROL_CHANNEL))) &&
-                          BRCMF_HDRS_ON(),
-                          frame, min_t(u16, len, 16), "TxHdr:\n");
-
-       /* Raise len to next SDIO block to eliminate tail command */
-       if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
-               u16 pad = bus->blocksize - (len % bus->blocksize);
-               if ((pad <= bus->roundup) && (pad < bus->blocksize))
-                               len += pad;
-       } else if (len % BRCMF_SDALIGN) {
-               len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
-       }
-
-       /* Some controllers have trouble with odd bytes -- round to even */
-       if (len & (ALIGNMENT - 1))
-                       len = roundup(len, ALIGNMENT);
+       __skb_queue_head_init(&localq);
+       __skb_queue_tail(&localq, pkt);
+       ret = brcmf_sdio_txpkt_prep(bus, &localq, chan);
+       if (ret)
+               goto done;
 
        sdio_claim_host(bus->sdiodev->func[1]);
        ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad,
-                                   SDIO_FUNC_2, F2SYNC, pkt);
+                                   SDIO_FUNC_2, F2SYNC, &localq);
        bus->sdcnt.f2txdata++;
 
        if (ret < 0) {
@@ -1865,11 +1975,11 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
        }
        sdio_release_host(bus->sdiodev->func[1]);
        if (ret == 0)
-               bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+               bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
 
 done:
-       /* restore pkt buffer pointer before calling tx complete routine */
-       skb_pull(pkt, SDPCM_HDRLEN + pad);
+       brcmf_sdio_txpkt_postp(bus, &localq);
+       __skb_dequeue_tail(&localq);
        brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0);
        return ret;
 }
@@ -1880,7 +1990,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
        u32 intstatus = 0;
        int ret = 0, prec_out;
        uint cnt = 0;
-       uint datalen;
        u8 tx_prec_map;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -1896,7 +2005,6 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
                        break;
                }
                spin_unlock_bh(&bus->txqlock);
-               datalen = pkt->len - SDPCM_HDRLEN;
 
                ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL);
 
@@ -2221,7 +2329,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                        }
 
                } else {
-                       bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+                       bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
                }
                sdio_release_host(bus->sdiodev->func[1]);
                bus->ctrl_frame_stat = false;
@@ -2276,13 +2384,14 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
+       ulong flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
        datalen = pkt->len;
 
        /* Add space for the header */
-       skb_push(pkt, SDPCM_HDRLEN);
+       skb_push(pkt, bus->tx_hdrlen);
        /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
 
        prec = prio2prec((pkt->priority & PRIOMASK));
@@ -2293,10 +2402,9 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        bus->sdcnt.fcqueued++;
 
        /* Priority based enq */
-       spin_lock_bh(&bus->txqlock);
+       spin_lock_irqsave(&bus->txqlock, flags);
        if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
-               skb_pull(pkt, SDPCM_HDRLEN);
-               brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
+               skb_pull(pkt, bus->tx_hdrlen);
                brcmf_err("out of bus->txq !!!\n");
                ret = -ENOSR;
        } else {
@@ -2307,7 +2415,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
                bus->txoff = true;
                brcmf_txflowblock(bus->sdiodev->dev, true);
        }
-       spin_unlock_bh(&bus->txqlock);
+       spin_unlock_irqrestore(&bus->txqlock, flags);
 
 #ifdef DEBUG
        if (pktq_plen(&bus->txq, prec) > qcount[prec])
@@ -2436,7 +2544,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
                return ret;
        }
 
-       bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+       bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
 
        return ret;
 }
@@ -2446,19 +2554,19 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 {
        u8 *frame;
        u16 len;
-       u32 swheader;
        uint retries = 0;
        u8 doff = 0;
        int ret = -1;
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
+       struct brcmf_sdio_hdrinfo hd_info = {0};
 
        brcmf_dbg(TRACE, "Enter\n");
 
        /* Back the pointer to make a room for bus header */
-       frame = msg - SDPCM_HDRLEN;
-       len = (msglen += SDPCM_HDRLEN);
+       frame = msg - bus->tx_hdrlen;
+       len = (msglen += bus->tx_hdrlen);
 
        /* Add alignment padding (optional for ctl frames) */
        doff = ((unsigned long)frame % BRCMF_SDALIGN);
@@ -2466,10 +2574,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                frame -= doff;
                len += doff;
                msglen += doff;
-               memset(frame, 0, doff + SDPCM_HDRLEN);
+               memset(frame, 0, doff + bus->tx_hdrlen);
        }
        /* precondition: doff < BRCMF_SDALIGN */
-       doff += SDPCM_HDRLEN;
+       doff += bus->tx_hdrlen;
 
        /* Round send length to next SDIO block */
        if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
@@ -2491,18 +2599,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
        brcmf_sdbrcm_bus_sleep(bus, false, false);
        sdio_release_host(bus->sdiodev->func[1]);
 
-       /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
-       *(__le16 *) frame = cpu_to_le16((u16) msglen);
-       *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen);
-
-       /* Software tag: channel, sequence number, data offset */
-       swheader =
-           ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
-            SDPCM_CHANNEL_MASK)
-           | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
-                            SDPCM_DOFFSET_MASK);
-       put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
-       put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+       hd_info.len = (u16)msglen;
+       hd_info.channel = SDPCM_CONTROL_CHANNEL;
+       hd_info.dat_offset = doff;
+       brcmf_sdio_hdpack(bus, frame, &hd_info);
 
        if (!data_ok(bus)) {
                brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
@@ -3733,7 +3833,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        struct brcmf_sdio *bus;
        struct brcmf_bus_dcmd *dlst;
        u32 dngl_txglom;
-       u32 dngl_txglomalign;
+       u32 txglomalign = 0;
        u8 idx;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -3752,7 +3852,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        bus->txbound = BRCMF_TXBOUND;
        bus->rxbound = BRCMF_RXBOUND;
        bus->txminmax = BRCMF_TXMINMAX;
-       bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+       bus->tx_seq = SDPCM_SEQ_WRAP - 1;
 
        INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
        bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
@@ -3794,8 +3894,11 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        bus->sdiodev->bus_if->chip = bus->ci->chip;
        bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
 
-       /* Attach to the brcmf/OS/network interface */
-       ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev);
+       /* default sdio bus header length for tx packet */
+       bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
+
+       /* Attach to the common layer, reserve hdr space */
+       ret = brcmf_attach(bus->tx_hdrlen, bus->sdiodev->dev);
        if (ret != 0) {
                brcmf_err("brcmf_attach failed\n");
                goto fail;
@@ -3827,9 +3930,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
                        dlst->param_len = sizeof(u32);
                } else {
                        /* otherwise, set txglomalign */
-                       dngl_txglomalign = bus->sdiodev->bus_if->align;
+                       if (sdiodev->pdata)
+                               txglomalign = sdiodev->pdata->sd_sgentry_align;
+                       /* SDIO ADMA requires at least 32 bit alignment */
+                       if (txglomalign < 4)
+                               txglomalign = 4;
                        dlst->name = "bus:txglomalign";
-                       dlst->param = (char *)&dngl_txglomalign;
+                       dlst->param = (char *)&txglomalign;
                        dlst->param_len = sizeof(u32);
                }
                list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list);
index 83ee53a..fad77dd 100644 (file)
@@ -185,6 +185,10 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
                  ifevent->action, ifevent->ifidx, ifevent->bssidx,
                  ifevent->flags, ifevent->role);
 
+       if (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) {
+               brcmf_dbg(EVENT, "event can be ignored\n");
+               return;
+       }
        if (ifevent->ifidx >= BRCMF_MAX_IFS) {
                brcmf_err("invalid interface index: %u\n",
                          ifevent->ifidx);
index 665ef69..ecabb04 100644 (file)
@@ -69,4 +69,25 @@ struct brcmf_fil_bss_enable_le {
        __le32 enable;
 };
 
+/**
+ * struct tdls_iovar - common structure for tdls iovars.
+ *
+ * @ea: ether address of peer station.
+ * @mode: mode value depending on specific tdls iovar.
+ * @chanspec: channel specification.
+ * @pad: unused (for future use).
+ */
+struct brcmf_tdls_iovar_le {
+       u8 ea[ETH_ALEN];                /* Station address */
+       u8 mode;                        /* mode: depends on iovar */
+       __le16 chanspec;
+       __le32 pad;                     /* future */
+};
+
+enum brcmf_tdls_manual_ep_ops {
+       BRCMF_TDLS_MANUAL_EP_CREATE = 1,
+       BRCMF_TDLS_MANUAL_EP_DELETE = 3,
+       BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
+};
+
 #endif /* FWIL_TYPES_H_ */
index 29b1f24..82f9140 100644 (file)
@@ -422,9 +422,12 @@ struct brcmf_fws_macdesc_table {
 
 struct brcmf_fws_info {
        struct brcmf_pub *drvr;
+       spinlock_t spinlock;
+       ulong flags;
        struct brcmf_fws_stats stats;
        struct brcmf_fws_hanger hanger;
        enum brcmf_fws_fcmode fcmode;
+       bool fw_signals;
        bool bcmc_credit_check;
        struct brcmf_fws_macdesc_table desc;
        struct workqueue_struct *fws_wq;
@@ -483,6 +486,18 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
 }
 #undef BRCMF_FWS_TLV_DEF
 
+static void brcmf_fws_lock(struct brcmf_fws_info *fws)
+               __acquires(&fws->spinlock)
+{
+       spin_lock_irqsave(&fws->spinlock, fws->flags);
+}
+
+static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
+               __releases(&fws->spinlock)
+{
+       spin_unlock_irqrestore(&fws->spinlock, fws->flags);
+}
+
 static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
 {
        u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
@@ -869,8 +884,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
                skcb->state = BRCMF_FWS_SKBSTATE_TIM;
                bus = fws->drvr->bus_if;
                err = brcmf_fws_hdrpush(fws, skb);
-               if (err == 0)
+               if (err == 0) {
+                       brcmf_fws_unlock(fws);
                        err = brcmf_bus_txdata(bus, skb);
+                       brcmf_fws_lock(fws);
+               }
                if (err)
                        brcmu_pkt_buf_free_skb(skb);
                return true;
@@ -905,26 +923,10 @@ static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
        return 0;
 }
 
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_lock(drvr, flags)                            \
-do {                                                           \
-       flags = 0;                                              \
-       spin_lock_irqsave(&((drvr)->fws_spinlock), (flags));    \
-} while (0)
-
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_unlock(drvr, flags) \
-       spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
-
 static
 int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry, *existing;
-       ulong flags;
        u8 mac_handle;
        u8 ifidx;
        u8 *addr;
@@ -938,10 +940,10 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
                if (entry->occupied) {
                        brcmf_dbg(TRACE, "deleting %s mac %pM\n",
                                  entry->name, addr);
-                       brcmf_fws_lock(fws->drvr, flags);
+                       brcmf_fws_lock(fws);
                        brcmf_fws_macdesc_cleanup(fws, entry, -1);
                        brcmf_fws_macdesc_deinit(entry);
-                       brcmf_fws_unlock(fws->drvr, flags);
+                       brcmf_fws_unlock(fws);
                } else
                        fws->stats.mac_update_failed++;
                return 0;
@@ -950,13 +952,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
        existing = brcmf_fws_macdesc_lookup(fws, addr);
        if (IS_ERR(existing)) {
                if (!entry->occupied) {
-                       brcmf_fws_lock(fws->drvr, flags);
+                       brcmf_fws_lock(fws);
                        entry->mac_handle = mac_handle;
                        brcmf_fws_macdesc_init(entry, addr, ifidx);
                        brcmf_fws_macdesc_set_name(fws, entry);
                        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                                        BRCMF_FWS_PSQ_LEN);
-                       brcmf_fws_unlock(fws->drvr, flags);
+                       brcmf_fws_unlock(fws);
                        brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
                } else {
                        fws->stats.mac_update_failed++;
@@ -964,13 +966,13 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
        } else {
                if (entry != existing) {
                        brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
-                       brcmf_fws_lock(fws->drvr, flags);
+                       brcmf_fws_lock(fws);
                        memcpy(entry, existing,
                               offsetof(struct brcmf_fws_mac_descriptor, psq));
                        entry->mac_handle = mac_handle;
                        brcmf_fws_macdesc_deinit(existing);
                        brcmf_fws_macdesc_set_name(fws, entry);
-                       brcmf_fws_unlock(fws->drvr, flags);
+                       brcmf_fws_unlock(fws);
                        brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
                                  addr);
                } else {
@@ -986,7 +988,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                                            u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
-       ulong flags;
        u8 mac_handle;
        int ret;
 
@@ -996,7 +997,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                fws->stats.mac_ps_update_failed++;
                return -ESRCH;
        }
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        /* a state update should wipe old credits */
        entry->requested_credit = 0;
        entry->requested_packet = 0;
@@ -1011,7 +1012,7 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
                ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
        }
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
        return ret;
 }
 
@@ -1019,7 +1020,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                                              u8 type, u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
-       ulong flags;
        u8 ifidx;
        int ret;
 
@@ -1038,7 +1038,7 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
 
        brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
                  entry->name);
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        switch (type) {
        case BRCMF_FWS_TYPE_INTERFACE_OPEN:
                entry->state = BRCMF_FWS_STATE_OPEN;
@@ -1050,10 +1050,10 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                break;
        default:
                ret = -EINVAL;
-               brcmf_fws_unlock(fws->drvr, flags);
+               brcmf_fws_unlock(fws);
                goto fail;
        }
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
        return ret;
 
 fail:
@@ -1065,7 +1065,6 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
                                      u8 *data)
 {
        struct brcmf_fws_mac_descriptor *entry;
-       ulong flags;
 
        entry = &fws->desc.nodes[data[1] & 0x1F];
        if (!entry->occupied) {
@@ -1079,14 +1078,14 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
        brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
                  brcmf_fws_get_tlv_name(type), type, entry->name,
                  data[0], data[2]);
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
                entry->requested_credit = data[0];
        else
                entry->requested_packet = data[0];
 
        entry->ac_bitmap = data[2];
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
@@ -1160,7 +1159,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
 static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
 {
        /* only schedule dequeue when there are credits for delayed traffic */
-       if (fws->fifo_credit_map & fws->fifo_delay_map)
+       if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
+           (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
                queue_work(fws->fws_wq, &fws->fws_dequeue_work);
 }
 
@@ -1383,7 +1383,6 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
 static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
                                             u8 *data)
 {
-       ulong flags;
        int i;
 
        if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
@@ -1392,19 +1391,18 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
        }
 
        brcmf_dbg(DATA, "enter: data %pM\n", data);
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
                brcmf_fws_return_credits(fws, i, data[i]);
 
        brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
                  fws->fifo_delay_map);
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
 static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
 {
-       ulong lflags;
        __le32 status_le;
        u32 status;
        u32 hslot;
@@ -1418,9 +1416,9 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
        hslot = brcmf_txstatus_get_field(status, HSLOT);
        genbit = brcmf_txstatus_get_field(status, GENERATION);
 
-       brcmf_fws_lock(fws->drvr, lflags);
+       brcmf_fws_lock(fws);
        brcmf_fws_txs_process(fws, flags, hslot, genbit);
-       brcmf_fws_unlock(fws->drvr, lflags);
+       brcmf_fws_unlock(fws);
        return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
 
@@ -1440,7 +1438,6 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
 {
        struct brcmf_fws_info *fws = ifp->drvr->fws;
        int i;
-       ulong flags;
        u8 *credits = data;
 
        if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
@@ -1453,7 +1450,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
        fws->creditmap_received = true;
 
        brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
-       brcmf_fws_lock(ifp->drvr, flags);
+       brcmf_fws_lock(fws);
        for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
                if (*credits)
                        fws->fifo_credit_map |= 1 << i;
@@ -1462,7 +1459,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                fws->fifo_credit[i] = *credits++;
        }
        brcmf_fws_schedule_deq(fws);
-       brcmf_fws_unlock(ifp->drvr, flags);
+       brcmf_fws_unlock(fws);
        return 0;
 }
 
@@ -1471,18 +1468,18 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
                                                void *data)
 {
        struct brcmf_fws_info *fws = ifp->drvr->fws;
-       ulong flags;
 
-       brcmf_fws_lock(ifp->drvr, flags);
+       brcmf_fws_lock(fws);
        if (fws)
                fws->bcmc_credit_check = true;
-       brcmf_fws_unlock(ifp->drvr, flags);
+       brcmf_fws_unlock(fws);
        return 0;
 }
 
 int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                      struct sk_buff *skb)
 {
+       struct brcmf_skb_reorder_data *rd;
        struct brcmf_fws_info *fws = drvr->fws;
        u8 *signal_data;
        s16 data_len;
@@ -1497,8 +1494,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
 
        WARN_ON(signal_len > skb->len);
 
+       if (!signal_len)
+               return 0;
        /* if flow control disabled, skip to packet data and leave */
-       if (!signal_len || !drvr->fw_signals) {
+       if (!fws->fw_signals) {
                skb_pull(skb, signal_len);
                return 0;
        }
@@ -1536,9 +1535,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
 
                err = BRCMF_FWS_RET_OK_NOSCHEDULE;
                switch (type) {
-               case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
                case BRCMF_FWS_TYPE_COMP_TXSTATUS:
                        break;
+               case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+                       rd = (struct brcmf_skb_reorder_data *)skb->cb;
+                       rd->reorder = data;
+                       break;
                case BRCMF_FWS_TYPE_MACDESC_ADD:
                case BRCMF_FWS_TYPE_MACDESC_DEL:
                        brcmf_fws_macdesc_indicate(fws, type, data);
@@ -1694,17 +1696,22 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
                return PTR_ERR(entry);
 
        brcmf_fws_precommit_skb(fws, fifo, skb);
+       entry->transit_count++;
+       if (entry->suppressed)
+               entry->suppr_transit_count++;
+       brcmf_fws_unlock(fws);
        rc = brcmf_bus_txdata(bus, skb);
+       brcmf_fws_lock(fws);
        brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
                  skcb->if_flags, skcb->htod, rc);
        if (rc < 0) {
+               entry->transit_count--;
+               if (entry->suppressed)
+                       entry->suppr_transit_count--;
                brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
                goto rollback;
        }
 
-       entry->transit_count++;
-       if (entry->suppressed)
-               entry->suppr_transit_count++;
        fws->stats.pkt2bus++;
        fws->stats.send_pkts[fifo]++;
        if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
@@ -1741,11 +1748,11 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        struct brcmf_fws_info *fws = drvr->fws;
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
        struct ethhdr *eh = (struct ethhdr *)(skb->data);
-       ulong flags;
        int fifo = BRCMF_FWS_FIFO_BCMC;
        bool multicast = is_multicast_ether_addr(eh->h_dest);
        bool pae = eh->h_proto == htons(ETH_P_PAE);
 
+       brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
        /* determine the priority */
        if (!skb->priority)
                skb->priority = cfg80211_classify8021d(skb);
@@ -1754,14 +1761,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        if (pae)
                atomic_inc(&ifp->pend_8021x_cnt);
 
-       if (!brcmf_fws_fc_active(fws)) {
-               /* If the protocol uses a data header, apply it */
-               brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
-
-               /* Use bus module to send data frame */
-               return brcmf_bus_txdata(drvr->bus_if, skb);
-       }
-
        /* set control buffer information */
        skcb->if_flags = 0;
        skcb->state = BRCMF_FWS_SKBSTATE_NEW;
@@ -1769,7 +1768,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        if (!multicast)
                fifo = brcmf_fws_prio2fifo[skb->priority];
 
-       brcmf_fws_lock(drvr, flags);
+       brcmf_fws_lock(fws);
        if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
                fws->borrow_defer_timestamp = jiffies +
                                              BRCMF_FWS_BORROW_DEFER_PERIOD;
@@ -1789,7 +1788,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                }
                brcmu_pkt_buf_free_skb(skb);
        }
-       brcmf_fws_unlock(drvr, flags);
+       brcmf_fws_unlock(fws);
        return 0;
 }
 
@@ -1809,7 +1808,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
        struct brcmf_fws_info *fws = ifp->drvr->fws;
        struct brcmf_fws_mac_descriptor *entry;
 
-       if (!ifp->ndev || !ifp->drvr->fw_signals)
+       if (!ifp->ndev)
                return;
 
        entry = &fws->desc.iface[ifp->ifidx];
@@ -1824,31 +1823,54 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
 void brcmf_fws_del_interface(struct brcmf_if *ifp)
 {
        struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
-       ulong flags;
 
        if (!entry)
                return;
 
-       brcmf_fws_lock(ifp->drvr, flags);
+       brcmf_fws_lock(ifp->drvr->fws);
        ifp->fws_desc = NULL;
        brcmf_dbg(TRACE, "deleting %s\n", entry->name);
        brcmf_fws_macdesc_deinit(entry);
        brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
-       brcmf_fws_unlock(ifp->drvr, flags);
+       brcmf_fws_unlock(ifp->drvr->fws);
 }
 
 static void brcmf_fws_dequeue_worker(struct work_struct *worker)
 {
        struct brcmf_fws_info *fws;
+       struct brcmf_pub *drvr;
        struct sk_buff *skb;
-       ulong flags;
        int fifo;
+       u32 hslot;
+       u32 ifidx;
+       int ret;
 
        fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+       drvr = fws->drvr;
 
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
             fifo--) {
+               if (!brcmf_fws_fc_active(fws)) {
+                       while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
+                               hslot = brcmf_skb_htod_tag_get_field(skb,
+                                                                    HSLOT);
+                               brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+                                                       &skb, true);
+                               ifidx = brcmf_skb_if_flags_get_field(skb,
+                                                                    INDEX);
+                               brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
+                               /* Use bus module to send data frame */
+                               brcmf_fws_unlock(fws);
+                               ret = brcmf_bus_txdata(drvr->bus_if, skb);
+                               brcmf_fws_lock(fws);
+                               if (ret < 0)
+                                       brcmf_txfinalize(drvr, skb, false);
+                               if (fws->bus_flow_blocked)
+                                       break;
+                       }
+                       continue;
+               }
                while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
                       (fifo == BRCMF_FWS_FIFO_BCMC))) {
                        skb = brcmf_fws_deq(fws, fifo);
@@ -1876,42 +1898,43 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                        }
                }
        }
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
 }
 
 int brcmf_fws_init(struct brcmf_pub *drvr)
 {
+       struct brcmf_fws_info *fws;
        u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
        int rc;
 
-       if (!drvr->fw_signals)
-               return 0;
-
-       spin_lock_init(&drvr->fws_spinlock);
-
        drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
        if (!drvr->fws) {
                rc = -ENOMEM;
                goto fail;
        }
 
+       fws = drvr->fws;
+
+       spin_lock_init(&fws->spinlock);
+
        /* set linkage back */
-       drvr->fws->drvr = drvr;
-       drvr->fws->fcmode = fcmode;
+       fws->drvr = drvr;
+       fws->fcmode = fcmode;
 
-       drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
-       if (drvr->fws->fws_wq == NULL) {
+       fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+       if (fws->fws_wq == NULL) {
                brcmf_err("workqueue creation failed\n");
                rc = -EBADF;
                goto fail;
        }
-       INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+       INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
 
        /* enable firmware signalling if fcmode active */
-       if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+       if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
                tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
                       BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
-                      BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
+                      BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
+                      BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
 
        rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
                                 brcmf_fws_notify_credit_map);
@@ -1927,31 +1950,33 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                goto fail;
        }
 
-       /* setting the iovar may fail if feature is unsupported
+       /* Setting the iovar may fail if feature is unsupported
         * so leave the rc as is so driver initialization can
-        * continue.
+        * continue. Set mode back to none indicating not enabled.
         */
+       fws->fw_signals = true;
        if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
                brcmf_err("failed to set bdcv2 tlv signaling\n");
-               goto fail_event;
+               fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+               fws->fw_signals = false;
        }
 
-       brcmf_fws_hanger_init(&drvr->fws->hanger);
-       brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
-       brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
-       brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+       if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
+               brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
+       brcmf_fws_hanger_init(&fws->hanger);
+       brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
+       brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
+       brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
-       brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
+       brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
 
        brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
-                 drvr->fw_signals ? "enabled" : "disabled", tlv);
+                 fws->fw_signals ? "enabled" : "disabled", tlv);
        return 0;
 
-fail_event:
-       brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
-       brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
 fail:
        brcmf_fws_deinit(drvr);
        return rc;
@@ -1960,24 +1985,18 @@ fail:
 void brcmf_fws_deinit(struct brcmf_pub *drvr)
 {
        struct brcmf_fws_info *fws = drvr->fws;
-       ulong flags;
 
        if (!fws)
                return;
 
-       /* disable firmware signalling entirely
-        * to avoid using the workqueue.
-        */
-       drvr->fw_signals = false;
-
        if (drvr->fws->fws_wq)
                destroy_workqueue(drvr->fws->fws_wq);
 
        /* cleanup */
-       brcmf_fws_lock(drvr, flags);
+       brcmf_fws_lock(fws);
        brcmf_fws_cleanup(fws, -1);
        drvr->fws = NULL;
-       brcmf_fws_unlock(drvr, flags);
+       brcmf_fws_unlock(fws);
 
        /* free top structure */
        kfree(fws);
@@ -1985,7 +2004,7 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
 
 bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
 {
-       if (!fws)
+       if (!fws->creditmap_received)
                return false;
 
        return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
@@ -1993,17 +2012,16 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
 
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
-       ulong flags;
        u32 hslot;
 
        if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
                brcmu_pkt_buf_free_skb(skb);
                return;
        }
-       brcmf_fws_lock(fws->drvr, flags);
+       brcmf_fws_lock(fws);
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
        brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0);
-       brcmf_fws_unlock(fws->drvr, flags);
+       brcmf_fws_unlock(fws);
 }
 
 void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
index 09786a5..2b5407f 100644 (file)
@@ -208,7 +208,7 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
  */
 extern int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff *pkt);
+                     uint flags, struct sk_buff_head *pktq);
 extern int
 brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, u8 *buf, uint nbytes);
index 322cadc..39e01a7 100644 (file)
@@ -614,7 +614,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        return 0;
 
 fail:
-       brcmf_txcomplete(dev, skb, false);
        return ret;
 }
 
index 7fa71f7..571f013 100644 (file)
@@ -3155,7 +3155,9 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
 }
 
 #ifdef CONFIG_NL80211_TESTMODE
-static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
+static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  void *data, int len)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = cfg_to_ndev(cfg);
@@ -4126,6 +4128,53 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
        clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
 }
 
+static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
+{
+       int ret;
+
+       switch (oper) {
+       case NL80211_TDLS_DISCOVERY_REQ:
+               ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
+               break;
+       case NL80211_TDLS_SETUP:
+               ret = BRCMF_TDLS_MANUAL_EP_CREATE;
+               break;
+       case NL80211_TDLS_TEARDOWN:
+               ret = BRCMF_TDLS_MANUAL_EP_DELETE;
+               break;
+       default:
+               brcmf_err("unsupported operation: %d\n", oper);
+               ret = -EOPNOTSUPP;
+       }
+       return ret;
+}
+
+static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
+                                   struct net_device *ndev, u8 *peer,
+                                   enum nl80211_tdls_operation oper)
+{
+       struct brcmf_if *ifp;
+       struct brcmf_tdls_iovar_le info;
+       int ret = 0;
+
+       ret = brcmf_convert_nl80211_tdls_oper(oper);
+       if (ret < 0)
+               return ret;
+
+       ifp = netdev_priv(ndev);
+       memset(&info, 0, sizeof(info));
+       info.mode = (u8)ret;
+       if (peer)
+               memcpy(info.ea, peer, ETH_ALEN);
+
+       ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
+                                      &info, sizeof(info));
+       if (ret < 0)
+               brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
+
+       return ret;
+}
+
 static struct cfg80211_ops wl_cfg80211_ops = {
        .add_virtual_intf = brcmf_cfg80211_add_iface,
        .del_virtual_intf = brcmf_cfg80211_del_iface,
@@ -4164,9 +4213,8 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .stop_p2p_device = brcmf_p2p_stop_device,
        .crit_proto_start = brcmf_cfg80211_crit_proto_start,
        .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
-#ifdef CONFIG_NL80211_TESTMODE
-       .testmode_cmd = brcmf_cfg80211_testmode
-#endif
+       .tdls_oper = brcmf_cfg80211_tdls_oper,
+       CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
 };
 
 static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type)
@@ -4287,7 +4335,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        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_HAS_REMAIN_ON_CHANNEL |
+                       WIPHY_FLAG_SUPPORTS_TDLS;
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        brcmf_wiphy_pno_params(wiphy);
@@ -4908,6 +4957,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
                goto cfg80211_p2p_attach_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;
+       }
+
        err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
                                    &io_type);
        if (err) {
index e4fd1ee..5336597 100644 (file)
@@ -679,27 +679,6 @@ bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode)
        return mode == BCMA_CLKMODE_FAST;
 }
 
-void ai_pci_up(struct si_pub *sih)
-{
-       struct si_info *sii;
-
-       sii = container_of(sih, struct si_info, pub);
-
-       if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI)
-               bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], true);
-}
-
-/* Unconfigure and/or apply various WARs when going down */
-void ai_pci_down(struct si_pub *sih)
-{
-       struct si_info *sii;
-
-       sii = container_of(sih, struct si_info, pub);
-
-       if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI)
-               bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], false);
-}
-
 /* Enable BT-COEX & Ex-PA for 4313 */
 void ai_epa_4313war(struct si_pub *sih)
 {
index 89562c1..a8a267b 100644 (file)
@@ -183,9 +183,6 @@ extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
 extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode);
 extern bool ai_deviceremoved(struct si_pub *sih);
 
-extern void ai_pci_down(struct si_pub *sih);
-extern void ai_pci_up(struct si_pub *sih);
-
 /* Enable Ex-PA for 4313 */
 extern void ai_epa_4313war(struct si_pub *sih);
 
index bd98285..fa391e4 100644 (file)
@@ -928,9 +928,9 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
                        }
                } else if (txs->phyerr) {
                        update_rate = false;
-                       brcms_err(wlc->hw->d11core,
-                                 "%s: ampdu tx phy error (0x%x)\n",
-                                 __func__, txs->phyerr);
+                       brcms_dbg_ht(wlc->hw->d11core,
+                                    "%s: ampdu tx phy error (0x%x)\n",
+                                    __func__, txs->phyerr);
                }
        }
 
index 1860c57..4fb9635 100644 (file)
@@ -1015,9 +1015,10 @@ static bool dma64_txidle(struct dma_info *di)
 
 /*
  * post receive buffers
- *  return false is refill failed completely and ring is empty this will stall
- *  the rx dma and user might want to call rxfill again asap. This unlikely
- *  happens on memory-rich NIC, but often on memory-constrained dongle
+ *  Return false if refill failed completely or dma mapping failed. The ring
+ *  is empty, which will stall the rx dma and user might want to call rxfill
+ *  again asap. This is unlikely to happen on a memory-rich NIC, but often on
+ *  memory-constrained dongle.
  */
 bool dma_rxfill(struct dma_pub *pub)
 {
@@ -1078,6 +1079,8 @@ bool dma_rxfill(struct dma_pub *pub)
 
                pa = dma_map_single(di->dmadev, p->data, di->rxbufsize,
                                    DMA_FROM_DEVICE);
+               if (dma_mapping_error(di->dmadev, pa))
+                       return false;
 
                /* save the free packet pointer */
                di->rxp[rxout] = p;
@@ -1284,7 +1287,11 @@ static void dma_txenq(struct dma_info *di, struct sk_buff *p)
 
        /* get physical address of buffer start */
        pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE);
-
+       /* if mapping failed, free skb */
+       if (dma_mapping_error(di->dmadev, pa)) {
+               brcmu_pkt_buf_free_skb(p);
+               return;
+       }
        /* With a DMA segment list, Descriptor table is filled
         * using the segment list instead of looping over
         * buffers in multi-chain DMA. Therefore, EOF for SGLIST
index 9fd6f2f..4608e0e 100644 (file)
@@ -882,8 +882,8 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
        mcl = le16_to_cpu(txh->MacTxControlLow);
 
        if (txs->phyerr)
-               brcms_err(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
-                         txs->phyerr, txh->MainRates);
+               brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
+                            txs->phyerr, txh->MainRates);
 
        if (txs->frameid != le16_to_cpu(txh->TxFrameID)) {
                brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n");
@@ -4652,7 +4652,9 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,
                wlc->band->phyrev = wlc_hw->band->phyrev;
                wlc->band->radioid = wlc_hw->band->radioid;
                wlc->band->radiorev = wlc_hw->band->radiorev;
-
+               brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit,
+                              wlc->band->phytype, wlc->band->phyrev,
+                              wlc->band->radioid, wlc->band->radiorev);
                /* default contention windows size limits */
                wlc_hw->band->CWmin = APHY_CWMIN;
                wlc_hw->band->CWmax = PHY_CWMAX;
@@ -4667,7 +4669,7 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,
        brcms_c_coredisable(wlc_hw);
 
        /* Match driver "down" state */
-       ai_pci_down(wlc_hw->sih);
+       bcma_core_pci_down(wlc_hw->d11core->bus);
 
        /* turn off pll and xtal to match driver "down" state */
        brcms_b_xtal(wlc_hw, OFF);
@@ -5010,12 +5012,12 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw)
         */
        if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
                /* put SB PCI in down state again */
-               ai_pci_down(wlc_hw->sih);
+               bcma_core_pci_down(wlc_hw->d11core->bus);
                brcms_b_xtal(wlc_hw, OFF);
                return -ENOMEDIUM;
        }
 
-       ai_pci_up(wlc_hw->sih);
+       bcma_core_pci_up(wlc_hw->d11core->bus);
 
        /* reset the d11 core */
        brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
@@ -5212,7 +5214,7 @@ static int brcms_b_down_finish(struct brcms_hardware *wlc_hw)
 
                /* turn off primary xtal and pll */
                if (!wlc_hw->noreset) {
-                       ai_pci_down(wlc_hw->sih);
+                       bcma_core_pci_down(wlc_hw->d11core->bus);
                        brcms_b_xtal(wlc_hw, OFF);
                }
        }
index 3d6b16c..b2d6d6d 100644 (file)
@@ -1137,8 +1137,9 @@ wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi,
        gain0_15 = ((biq1 & 0xf) << 12) |
                   ((tia & 0xf) << 8) |
                   ((lna2 & 0x3) << 6) |
-                  ((lna2 &
-                    0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
+                  ((lna2 & 0x3) << 4) |
+                  ((lna1 & 0x3) << 2) |
+                  ((lna1 & 0x3) << 0);
 
        mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
        mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
@@ -1328,6 +1329,43 @@ static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples)
        return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
 }
 
+static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
+                                     u16 tia_gain, u16 lna2_gain)
+{
+       u32 i_thresh_l, q_thresh_l;
+       u32 i_thresh_h, q_thresh_h;
+       struct lcnphy_iq_est iq_est_h, iq_est_l;
+
+       wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain,
+                                              lna2_gain, 0);
+
+       wlc_lcnphy_rx_gain_override_enable(pi, true);
+       wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
+       udelay(500);
+       write_radio_reg(pi, RADIO_2064_REG112, 0);
+       if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
+               return false;
+
+       wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
+       udelay(500);
+       write_radio_reg(pi, RADIO_2064_REG112, 0);
+       if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
+               return false;
+
+       i_thresh_l = (iq_est_l.i_pwr << 1);
+       i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr;
+
+       q_thresh_l = (iq_est_l.q_pwr << 1);
+       q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr;
+       if ((iq_est_h.i_pwr > i_thresh_l) &&
+           (iq_est_h.i_pwr < i_thresh_h) &&
+           (iq_est_h.q_pwr > q_thresh_l) &&
+           (iq_est_h.q_pwr < q_thresh_h))
+               return true;
+
+       return false;
+}
+
 static bool
 wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
                     const struct lcnphy_rx_iqcomp *iqcomp,
@@ -1342,8 +1380,8 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
            RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
            rfoverride3_old, rfoverride3val_old, rfoverride4_old,
            rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
-       int tia_gain;
-       u32 received_power, rx_pwr_threshold;
+       int tia_gain, lna2_gain, biq1_gain;
+       bool set_gain;
        u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
        u16 values_to_save[11];
        s16 *ptr;
@@ -1368,126 +1406,125 @@ wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
                goto cal_done;
        }
 
-       if (module == 1) {
+       WARN_ON(module != 1);
+       tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
+       wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
 
-               tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
-               wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
+       for (i = 0; i < 11; i++)
+               values_to_save[i] =
+                       read_radio_reg(pi, rxiq_cal_rf_reg[i]);
+       Core1TxControl_old = read_phy_reg(pi, 0x631);
+
+       or_phy_reg(pi, 0x631, 0x0015);
+
+       RFOverride0_old = read_phy_reg(pi, 0x44c);
+       RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
+       rfoverride2_old = read_phy_reg(pi, 0x4b0);
+       rfoverride2val_old = read_phy_reg(pi, 0x4b1);
+       rfoverride3_old = read_phy_reg(pi, 0x4f9);
+       rfoverride3val_old = read_phy_reg(pi, 0x4fa);
+       rfoverride4_old = read_phy_reg(pi, 0x938);
+       rfoverride4val_old = read_phy_reg(pi, 0x939);
+       afectrlovr_old = read_phy_reg(pi, 0x43b);
+       afectrlovrval_old = read_phy_reg(pi, 0x43c);
+       old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+       old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
 
-               for (i = 0; i < 11; i++)
-                       values_to_save[i] =
-                               read_radio_reg(pi, rxiq_cal_rf_reg[i]);
-               Core1TxControl_old = read_phy_reg(pi, 0x631);
-
-               or_phy_reg(pi, 0x631, 0x0015);
-
-               RFOverride0_old = read_phy_reg(pi, 0x44c);
-               RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
-               rfoverride2_old = read_phy_reg(pi, 0x4b0);
-               rfoverride2val_old = read_phy_reg(pi, 0x4b1);
-               rfoverride3_old = read_phy_reg(pi, 0x4f9);
-               rfoverride3val_old = read_phy_reg(pi, 0x4fa);
-               rfoverride4_old = read_phy_reg(pi, 0x938);
-               rfoverride4val_old = read_phy_reg(pi, 0x939);
-               afectrlovr_old = read_phy_reg(pi, 0x43b);
-               afectrlovrval_old = read_phy_reg(pi, 0x43c);
-               old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
-               old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
-
-               tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
-               if (tx_gain_override_old) {
-                       wlc_lcnphy_get_tx_gain(pi, &old_gains);
-                       tx_gain_index_old = pi_lcn->lcnphy_current_index;
-               }
+       tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
+       if (tx_gain_override_old) {
+               wlc_lcnphy_get_tx_gain(pi, &old_gains);
+               tx_gain_index_old = pi_lcn->lcnphy_current_index;
+       }
 
-               wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
+       wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
 
-               mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
-               mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
+       mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
+       mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
 
-               mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
-               mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+       mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
+       mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
 
-               write_radio_reg(pi, RADIO_2064_REG116, 0x06);
-               write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
-               write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
-               write_radio_reg(pi, RADIO_2064_REG098, 0x03);
-               write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
-               mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
-               write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
-               write_radio_reg(pi, RADIO_2064_REG114, 0x01);
-               write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
-               write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
-
-               mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
-               mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
-               mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
-               mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
-               mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
-               mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
-               mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
-               mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
-               mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
-               mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
-
-               mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
-               mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
-
-               wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
-               write_phy_reg(pi, 0x6da, 0xffff);
-               or_phy_reg(pi, 0x6db, 0x3);
-               wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
-               wlc_lcnphy_rx_gain_override_enable(pi, true);
-
-               tia_gain = 8;
-               rx_pwr_threshold = 950;
-               while (tia_gain > 0) {
-                       tia_gain -= 1;
-                       wlc_lcnphy_set_rx_gain_by_distribution(pi,
-                                                              0, 0, 2, 2,
-                                                              (u16)
-                                                              tia_gain, 1, 0);
-                       udelay(500);
+       write_radio_reg(pi, RADIO_2064_REG116, 0x06);
+       write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
+       write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
+       write_radio_reg(pi, RADIO_2064_REG098, 0x03);
+       write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
+       mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
+       write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
+       write_radio_reg(pi, RADIO_2064_REG114, 0x01);
+       write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
+       write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
+
+       mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
+       mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
+       mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
+       mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
+       mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
+       mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
+       mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
+       mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
+       mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
+       mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
 
-                       received_power =
-                               wlc_lcnphy_measure_digital_power(pi, 2000);
-                       if (received_power < rx_pwr_threshold)
-                               break;
-               }
-               result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
+       mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
+       mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
 
-               wlc_lcnphy_stop_tx_tone(pi);
+       write_phy_reg(pi, 0x6da, 0xffff);
+       or_phy_reg(pi, 0x6db, 0x3);
+
+       wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
+       for (lna2_gain = 3; lna2_gain >= 0; lna2_gain--) {
+               for (tia_gain = 4; tia_gain >= 0; tia_gain--) {
+                       for (biq1_gain = 6; biq1_gain >= 0; biq1_gain--) {
+                               set_gain = wlc_lcnphy_rx_iq_cal_gain(pi,
+                                                                    (u16)
+                                                                    biq1_gain,
+                                                                    (u16)
+                                                                    tia_gain,
+                                                                    (u16)
+                                                                    lna2_gain);
+                               if (!set_gain)
+                                       continue;
+
+                               result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024);
+                               goto stop_tone;
+                       }
+               }
+       }
 
-               write_phy_reg(pi, 0x631, Core1TxControl_old);
+stop_tone:
+       wlc_lcnphy_stop_tx_tone(pi);
 
-               write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
-               write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
-               write_phy_reg(pi, 0x4b0, rfoverride2_old);
-               write_phy_reg(pi, 0x4b1, rfoverride2val_old);
-               write_phy_reg(pi, 0x4f9, rfoverride3_old);
-               write_phy_reg(pi, 0x4fa, rfoverride3val_old);
-               write_phy_reg(pi, 0x938, rfoverride4_old);
-               write_phy_reg(pi, 0x939, rfoverride4val_old);
-               write_phy_reg(pi, 0x43b, afectrlovr_old);
-               write_phy_reg(pi, 0x43c, afectrlovrval_old);
-               write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
-               write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
+       write_phy_reg(pi, 0x631, Core1TxControl_old);
+
+       write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
+       write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
+       write_phy_reg(pi, 0x4b0, rfoverride2_old);
+       write_phy_reg(pi, 0x4b1, rfoverride2val_old);
+       write_phy_reg(pi, 0x4f9, rfoverride3_old);
+       write_phy_reg(pi, 0x4fa, rfoverride3val_old);
+       write_phy_reg(pi, 0x938, rfoverride4_old);
+       write_phy_reg(pi, 0x939, rfoverride4val_old);
+       write_phy_reg(pi, 0x43b, afectrlovr_old);
+       write_phy_reg(pi, 0x43c, afectrlovrval_old);
+       write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
+       write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
 
-               wlc_lcnphy_clear_trsw_override(pi);
+       wlc_lcnphy_clear_trsw_override(pi);
 
-               mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
+       mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
 
-               for (i = 0; i < 11; i++)
-                       write_radio_reg(pi, rxiq_cal_rf_reg[i],
-                                       values_to_save[i]);
+       for (i = 0; i < 11; i++)
+               write_radio_reg(pi, rxiq_cal_rf_reg[i],
+                               values_to_save[i]);
 
-               if (tx_gain_override_old)
-                       wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
-               else
-                       wlc_lcnphy_disable_tx_gain_override(pi);
+       if (tx_gain_override_old)
+               wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
+       else
+               wlc_lcnphy_disable_tx_gain_override(pi);
 
-               wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
-               wlc_lcnphy_rx_gain_override_enable(pi, false);
-       }
+       wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
+       wlc_lcnphy_rx_gain_override_enable(pi, false);
 
 cal_done:
        kfree(ptr);
@@ -1789,6 +1826,19 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
                write_radio_reg(pi, RADIO_2064_REG038, 3);
                write_radio_reg(pi, RADIO_2064_REG091, 7);
        }
+
+       if (!(pi->sh->boardflags & BFL_FEM)) {
+               static const u8 reg038[14] = {
+                       0xd, 0xe, 0xd, 0xd, 0xd, 0xc, 0xa,
+                       0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0
+               };
+
+               write_radio_reg(pi, RADIO_2064_REG02A, 0xf);
+               write_radio_reg(pi, RADIO_2064_REG091, 0x3);
+               write_radio_reg(pi, RADIO_2064_REG038, 0x3);
+
+               write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]);
+       }
 }
 
 static int
@@ -1983,6 +2033,16 @@ wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos)
                } else {
                        mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1);
                        mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+                       mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0);
+                       mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2);
+                       mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0);
+                       mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4);
+                       mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+                       mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77);
+                       mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1);
+                       mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7);
+                       mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1);
+                       mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4);
                }
        } else {
                mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2);
@@ -2069,13 +2129,23 @@ static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi)
                    (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12));
 
        mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
+       mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0));
 }
 
 static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
 {
        struct phytbl_info tab;
        u32 rfseq, ind;
+       enum lcnphy_tssi_mode mode;
+       u8 tssi_sel;
 
+       if (pi->sh->boardflags & BFL_FEM) {
+               tssi_sel = 0x1;
+               mode = LCNPHY_TSSI_EXT;
+       } else {
+               tssi_sel = 0xe;
+               mode = LCNPHY_TSSI_POST_PA;
+       }
        tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
        tab.tbl_width = 32;
        tab.tbl_ptr = &ind;
@@ -2096,7 +2166,7 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
 
        mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4);
 
-       wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
+       wlc_lcnphy_set_tssi_mux(pi, mode);
        mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14);
 
        mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15);
@@ -2132,9 +2202,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
        mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0);
 
        if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
-               mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe);
+               mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel);
                mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4);
        } else {
+               mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1);
                mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1);
                mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3);
        }
@@ -2181,6 +2252,10 @@ static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
 
        mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8);
 
+       mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0);
+       mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+       mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+
        wlc_lcnphy_pwrctrl_rssiparams(pi);
 }
 
@@ -2799,6 +2874,8 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
                read_radio_reg(pi, RADIO_2064_REG007) & 1;
        u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
        u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
+       u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi);
+
        idleTssi = read_phy_reg(pi, 0x4ab);
        suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) &
                         MCTL_EN_MAC));
@@ -2816,6 +2893,12 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
        mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4);
        mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2);
        wlc_lcnphy_tssi_setup(pi);
+
+       mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0));
+       mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6));
+
+       wlc_lcnphy_set_bbmult(pi, 0x0);
+
        wlc_phy_do_dummy_tx(pi, true, OFF);
        idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
                    >> 0);
@@ -2837,6 +2920,7 @@ static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi)
 
        mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12);
 
+       wlc_lcnphy_set_bbmult(pi, SAVE_bbmult);
        wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old);
        wlc_lcnphy_set_tx_gain(pi, &old_gains);
        wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl);
@@ -3050,6 +3134,11 @@ static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi)
                        wlc_lcnphy_write_table(pi, &tab);
                        tab.tbl_offset++;
                }
+               mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0);
+               mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0);
+               mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8);
+               mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4);
+               mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2);
 
                mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7);
 
@@ -3851,7 +3940,6 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
        target_gains.pad_gain = 21;
        target_gains.dac_gain = 0;
        wlc_lcnphy_set_tx_gain(pi, &target_gains);
-       wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
 
        if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) {
 
@@ -3862,6 +3950,7 @@ static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi)
                                        lcnphy_recal ? LCNPHY_CAL_RECAL :
                                        LCNPHY_CAL_FULL), false);
        } else {
+               wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
                wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
        }
 
@@ -4283,20 +4372,20 @@ wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi,
        u16 pa_gain;
        u16 gm_gain;
 
-       if (CHSPEC_IS5G(pi->radio_chanspec))
-               pa_gain = 0x70;
-       else
-               pa_gain = 0x70;
-
        if (pi->sh->boardflags & BFL_FEM)
                pa_gain = 0x10;
+       else
+               pa_gain = 0x60;
        tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
        tab.tbl_width = 32;
        tab.tbl_len = 1;
        tab.tbl_ptr = &val;
 
+       /* fixed gm_gain value for iPA */
+       gm_gain = 15;
        for (j = 0; j < 128; j++) {
-               gm_gain = gain_table[j].gm;
+               if (pi->sh->boardflags & BFL_FEM)
+                       gm_gain = gain_table[j].gm;
                val = (((u32) pa_gain << 24) |
                       (gain_table[j].pad << 16) |
                       (gain_table[j].pga << 8) | gm_gain);
@@ -4507,7 +4596,10 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
 
        write_phy_reg(pi, 0x4ea, 0x4688);
 
-       mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+       if (pi->sh->boardflags & BFL_FEM)
+               mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+       else
+               mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0);
 
        mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
 
@@ -4518,6 +4610,13 @@ static void wlc_radio_2064_init(struct brcms_phy *pi)
        wlc_lcnphy_rcal(pi);
 
        wlc_lcnphy_rc_cal(pi);
+
+       if (!(pi->sh->boardflags & BFL_FEM)) {
+               write_radio_reg(pi, RADIO_2064_REG032, 0x6f);
+               write_radio_reg(pi, RADIO_2064_REG033, 0x19);
+               write_radio_reg(pi, RADIO_2064_REG039, 0xe);
+       }
+
 }
 
 static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
@@ -4530,6 +4629,7 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
        uint idx;
        u8 phybw40;
        struct phytbl_info tab;
+       const struct phytbl_info *tb;
        u32 val;
 
        phybw40 = CHSPEC_IS40(pi->radio_chanspec);
@@ -4547,22 +4647,20 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
                wlc_lcnphy_write_table(pi, &tab);
        }
 
-       tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
-       tab.tbl_width = 16;
-       tab.tbl_ptr = &val;
-       tab.tbl_len = 1;
-
-       val = 114;
-       tab.tbl_offset = 0;
-       wlc_lcnphy_write_table(pi, &tab);
+       if (!(pi->sh->boardflags & BFL_FEM)) {
+               tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
+               tab.tbl_width = 16;
+               tab.tbl_ptr = &val;
+               tab.tbl_len = 1;
 
-       val = 130;
-       tab.tbl_offset = 1;
-       wlc_lcnphy_write_table(pi, &tab);
+               val = 150;
+               tab.tbl_offset = 0;
+               wlc_lcnphy_write_table(pi, &tab);
 
-       val = 6;
-       tab.tbl_offset = 8;
-       wlc_lcnphy_write_table(pi, &tab);
+               val = 220;
+               tab.tbl_offset = 1;
+               wlc_lcnphy_write_table(pi, &tab);
+       }
 
        if (CHSPEC_IS2G(pi->radio_chanspec)) {
                if (pi->sh->boardflags & BFL_FEM)
@@ -4576,7 +4674,6 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
        }
 
        if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
-               const struct phytbl_info *tb;
                int l;
 
                if (CHSPEC_IS2G(pi->radio_chanspec)) {
@@ -4597,21 +4694,22 @@ static void wlc_lcnphy_tbl_init(struct brcms_phy *pi)
                        wlc_lcnphy_write_table(pi, &tb[idx]);
        }
 
-       if ((pi->sh->boardflags & BFL_FEM)
-           && !(pi->sh->boardflags & BFL_FEM_BT))
-               wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
-       else if (pi->sh->boardflags & BFL_FEM_BT) {
-               if (pi->sh->boardrev < 0x1250)
-                       wlc_lcnphy_write_table(
-                               pi,
-                               &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
+       if (pi->sh->boardflags & BFL_FEM) {
+               if (pi->sh->boardflags & BFL_FEM_BT) {
+                       if (pi->sh->boardrev < 0x1250)
+                               tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
+                       else
+                               tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
+               } else {
+                       tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa;
+               }
+       } else {
+               if (pi->sh->boardflags & BFL_FEM_BT)
+                       tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
                else
-                       wlc_lcnphy_write_table(
-                               pi,
-                               &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
-       } else
-               wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
-
+                       tb = &dot11lcn_sw_ctrl_tbl_info_4313;
+       }
+       wlc_lcnphy_write_table(pi, tb);
        wlc_lcnphy_load_rfpower(pi);
 
        wlc_lcnphy_clear_papd_comptable(pi);
@@ -4955,6 +5053,8 @@ void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec)
                wlc_lcnphy_load_tx_iir_filter(pi, true, 3);
 
        mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
+       if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
+               wlc_lcnphy_tssi_setup(pi);
 }
 
 void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
@@ -4993,8 +5093,7 @@ bool wlc_phy_attach_lcnphy(struct brcms_phy *pi)
        if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
                return false;
 
-       if ((pi->sh->boardflags & BFL_FEM) &&
-           (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
+       if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
                if (pi_lcn->lcnphy_tempsense_option == 3) {
                        pi->hwpwrctrl = true;
                        pi->hwpwrctrl_capable = true;
index 622c01c..d7fa312 100644 (file)
@@ -1507,117 +1507,103 @@ static const u32 dot11lcn_gain_tbl_5G[] = {
 
 const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = {
        {&dot11lcn_gain_tbl_rev0,
-        sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18,
+        ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18,
         0, 32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
        ,
 };
 
 static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = {
        {&dot11lcn_gain_tbl_rev1,
-        sizeof(dot11lcn_gain_tbl_rev1) / sizeof(dot11lcn_gain_tbl_rev1[0]), 18,
+        ARRAY_SIZE(dot11lcn_gain_tbl_rev1), 18,
         0, 32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
        ,
 };
 
 const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = {
        {&dot11lcn_gain_tbl_2G,
-        sizeof(dot11lcn_gain_tbl_2G) / sizeof(dot11lcn_gain_tbl_2G[0]), 18, 0,
+        ARRAY_SIZE(dot11lcn_gain_tbl_2G), 18, 0,
         32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_2G,
-        sizeof(dot11lcn_aux_gain_idx_tbl_2G) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_2G[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_2G), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_2G,
-        sizeof(dot11lcn_gain_idx_tbl_2G) / sizeof(dot11lcn_gain_idx_tbl_2G[0]),
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_2G),
         13, 0, 32}
        ,
        {&dot11lcn_gain_val_tbl_2G,
-        sizeof(dot11lcn_gain_val_tbl_2G) / sizeof(dot11lcn_gain_val_tbl_2G[0]),
+        ARRAY_SIZE(dot11lcn_gain_val_tbl_2G),
         17, 0, 8}
 };
 
 const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = {
        {&dot11lcn_gain_tbl_5G,
-        sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
+        ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0,
         32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_5G,
-        sizeof(dot11lcn_aux_gain_idx_tbl_5G) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_5G,
-        sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]),
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G),
         13, 0, 32}
        ,
        {&dot11lcn_gain_val_tbl_5G,
-        sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]),
+        ARRAY_SIZE(dot11lcn_gain_val_tbl_5G),
         17, 0, 8}
 };
 
 const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = {
        {&dot11lcn_gain_tbl_extlna_2G,
-        sizeof(dot11lcn_gain_tbl_extlna_2G) /
-        sizeof(dot11lcn_gain_tbl_extlna_2G[0]), 18, 0, 32}
+        ARRAY_SIZE(dot11lcn_gain_tbl_extlna_2G), 18, 0, 32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_extlna_2G,
-        sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_extlna_2G), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_extlna_2G,
-        sizeof(dot11lcn_gain_idx_tbl_extlna_2G) /
-        sizeof(dot11lcn_gain_idx_tbl_extlna_2G[0]), 13, 0, 32}
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_extlna_2G), 13, 0, 32}
        ,
        {&dot11lcn_gain_val_tbl_extlna_2G,
-        sizeof(dot11lcn_gain_val_tbl_extlna_2G) /
-        sizeof(dot11lcn_gain_val_tbl_extlna_2G[0]), 17, 0, 8}
+        ARRAY_SIZE(dot11lcn_gain_val_tbl_extlna_2G), 17, 0, 8}
 };
 
 const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = {
        {&dot11lcn_gain_tbl_5G,
-        sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
+        ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0,
         32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_5G,
-        sizeof(dot11lcn_aux_gain_idx_tbl_5G) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16}
        ,
        {&dot11lcn_gain_idx_tbl_5G,
-        sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]),
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G),
         13, 0, 32}
        ,
        {&dot11lcn_gain_val_tbl_5G,
-        sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]),
+        ARRAY_SIZE(dot11lcn_gain_val_tbl_5G),
         17, 0, 8}
 };
 
 const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 =
-       sizeof(dot11lcnphytbl_rx_gain_info_rev0) /
-       sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]);
+       ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_rev0);
 
 const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz =
-       sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) /
-       sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]);
+       ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_2G_rev2);
 
 const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz =
-       sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) /
-       sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]);
+       ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_5G_rev2);
 
 static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = {
        0x014d,
@@ -2058,6 +2044,73 @@ static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = {
        0x0005,
 };
 
+static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = {
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+       0x0005,
+       0x0006,
+       0x0009,
+       0x000a,
+};
+
 static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
        0x0004,
        0x0004,
@@ -2771,89 +2824,79 @@ static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = {
 
 const struct phytbl_info dot11lcnphytbl_info_rev0[] = {
        {&dot11lcn_min_sig_sq_tbl_rev0,
-        sizeof(dot11lcn_min_sig_sq_tbl_rev0) /
-        sizeof(dot11lcn_min_sig_sq_tbl_rev0[0]), 2, 0, 16}
+        ARRAY_SIZE(dot11lcn_min_sig_sq_tbl_rev0), 2, 0, 16}
        ,
        {&dot11lcn_noise_scale_tbl_rev0,
-        sizeof(dot11lcn_noise_scale_tbl_rev0) /
-        sizeof(dot11lcn_noise_scale_tbl_rev0[0]), 1, 0, 16}
+        ARRAY_SIZE(dot11lcn_noise_scale_tbl_rev0), 1, 0, 16}
        ,
        {&dot11lcn_fltr_ctrl_tbl_rev0,
-        sizeof(dot11lcn_fltr_ctrl_tbl_rev0) /
-        sizeof(dot11lcn_fltr_ctrl_tbl_rev0[0]), 11, 0, 32}
+        ARRAY_SIZE(dot11lcn_fltr_ctrl_tbl_rev0), 11, 0, 32}
        ,
        {&dot11lcn_ps_ctrl_tbl_rev0,
-        sizeof(dot11lcn_ps_ctrl_tbl_rev0) /
-        sizeof(dot11lcn_ps_ctrl_tbl_rev0[0]), 12, 0, 32}
+        ARRAY_SIZE(dot11lcn_ps_ctrl_tbl_rev0), 12, 0, 32}
        ,
        {&dot11lcn_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+        ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
        ,
        {&dot11lcn_aux_gain_idx_tbl_rev0,
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
-        sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+        ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
        ,
        {&dot11lcn_sw_ctrl_tbl_rev0,
-        sizeof(dot11lcn_sw_ctrl_tbl_rev0) /
-        sizeof(dot11lcn_sw_ctrl_tbl_rev0[0]), 15, 0, 16}
+        ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_rev0), 15, 0, 16}
        ,
        {&dot11lcn_nf_table_rev0,
-        sizeof(dot11lcn_nf_table_rev0) / sizeof(dot11lcn_nf_table_rev0[0]), 16,
+        ARRAY_SIZE(dot11lcn_nf_table_rev0), 16,
         0, 8}
        ,
        {&dot11lcn_gain_val_tbl_rev0,
-        sizeof(dot11lcn_gain_val_tbl_rev0) /
-        sizeof(dot11lcn_gain_val_tbl_rev0[0]), 17, 0, 8}
+        ARRAY_SIZE(dot11lcn_gain_val_tbl_rev0), 17, 0, 8}
        ,
        {&dot11lcn_gain_tbl_rev0,
-        sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18,
+        ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18,
         0, 32}
        ,
        {&dot11lcn_spur_tbl_rev0,
-        sizeof(dot11lcn_spur_tbl_rev0) / sizeof(dot11lcn_spur_tbl_rev0[0]), 20,
+        ARRAY_SIZE(dot11lcn_spur_tbl_rev0), 20,
         0, 8}
        ,
        {&dot11lcn_unsup_mcs_tbl_rev0,
-        sizeof(dot11lcn_unsup_mcs_tbl_rev0) /
-        sizeof(dot11lcn_unsup_mcs_tbl_rev0[0]), 23, 0, 16}
+        ARRAY_SIZE(dot11lcn_unsup_mcs_tbl_rev0), 23, 0, 16}
        ,
        {&dot11lcn_iq_local_tbl_rev0,
-        sizeof(dot11lcn_iq_local_tbl_rev0) /
-        sizeof(dot11lcn_iq_local_tbl_rev0[0]), 0, 0, 16}
+        ARRAY_SIZE(dot11lcn_iq_local_tbl_rev0), 0, 0, 16}
        ,
        {&dot11lcn_papd_compdelta_tbl_rev0,
-        sizeof(dot11lcn_papd_compdelta_tbl_rev0) /
-        sizeof(dot11lcn_papd_compdelta_tbl_rev0[0]), 24, 0, 32}
+        ARRAY_SIZE(dot11lcn_papd_compdelta_tbl_rev0), 24, 0, 32}
        ,
 };
 
 const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
        &dot11lcn_sw_ctrl_tbl_4313_rev0,
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) /
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
+       ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_rev0), 15, 0, 16
+};
+
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = {
+       &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo,
+       ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo), 15, 0, 16
 };
 
 const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = {
        &dot11lcn_sw_ctrl_tbl_4313_epa_rev0,
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) /
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16
+       ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0), 15, 0, 16
 };
 
 const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = {
        &dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo,
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) /
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16
+       ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo), 15, 0, 16
 };
 
 const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = {
        &dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0,
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) /
-       sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16
+       ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0), 15, 0, 16
 };
 
 const u32 dot11lcnphytbl_info_sz_rev0 =
-       sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]);
+       ARRAY_SIZE(dot11lcnphytbl_info_rev0);
 
 const struct lcnphy_tx_gain_tbl_entry
 dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
@@ -2988,134 +3031,134 @@ dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
 };
 
 const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = {
-       {7, 0, 31, 0, 72},
-       {7, 0, 31, 0, 70},
-       {7, 0, 31, 0, 68},
-       {7, 0, 30, 0, 67},
-       {7, 0, 29, 0, 68},
-       {7, 0, 28, 0, 68},
-       {7, 0, 27, 0, 69},
-       {7, 0, 26, 0, 70},
-       {7, 0, 25, 0, 70},
-       {7, 0, 24, 0, 71},
-       {7, 0, 23, 0, 72},
-       {7, 0, 23, 0, 70},
-       {7, 0, 22, 0, 71},
-       {7, 0, 21, 0, 72},
-       {7, 0, 21, 0, 70},
-       {7, 0, 21, 0, 68},
-       {7, 0, 21, 0, 66},
-       {7, 0, 21, 0, 64},
-       {7, 0, 21, 0, 63},
-       {7, 0, 20, 0, 64},
-       {7, 0, 19, 0, 65},
-       {7, 0, 19, 0, 64},
-       {7, 0, 18, 0, 65},
-       {7, 0, 18, 0, 64},
-       {7, 0, 17, 0, 65},
-       {7, 0, 17, 0, 64},
-       {7, 0, 16, 0, 65},
-       {7, 0, 16, 0, 64},
-       {7, 0, 16, 0, 62},
-       {7, 0, 16, 0, 60},
-       {7, 0, 16, 0, 58},
-       {7, 0, 15, 0, 61},
-       {7, 0, 15, 0, 59},
-       {7, 0, 14, 0, 61},
-       {7, 0, 14, 0, 60},
-       {7, 0, 14, 0, 58},
-       {7, 0, 13, 0, 60},
-       {7, 0, 13, 0, 59},
-       {7, 0, 12, 0, 62},
-       {7, 0, 12, 0, 60},
-       {7, 0, 12, 0, 58},
-       {7, 0, 11, 0, 62},
-       {7, 0, 11, 0, 60},
-       {7, 0, 11, 0, 59},
-       {7, 0, 11, 0, 57},
-       {7, 0, 10, 0, 61},
-       {7, 0, 10, 0, 59},
-       {7, 0, 10, 0, 57},
-       {7, 0, 9, 0, 62},
-       {7, 0, 9, 0, 60},
-       {7, 0, 9, 0, 58},
-       {7, 0, 9, 0, 57},
-       {7, 0, 8, 0, 62},
-       {7, 0, 8, 0, 60},
-       {7, 0, 8, 0, 58},
-       {7, 0, 8, 0, 57},
-       {7, 0, 8, 0, 55},
-       {7, 0, 7, 0, 61},
+       {15, 0, 31, 0, 72},
+       {15, 0, 31, 0, 70},
+       {15, 0, 31, 0, 68},
+       {15, 0, 30, 0, 68},
+       {15, 0, 29, 0, 69},
+       {15, 0, 28, 0, 69},
+       {15, 0, 27, 0, 70},
+       {15, 0, 26, 0, 70},
+       {15, 0, 25, 0, 71},
+       {15, 0, 24, 0, 72},
+       {15, 0, 23, 0, 73},
+       {15, 0, 23, 0, 71},
+       {15, 0, 22, 0, 72},
+       {15, 0, 21, 0, 73},
+       {15, 0, 21, 0, 71},
+       {15, 0, 21, 0, 69},
+       {15, 0, 21, 0, 67},
+       {15, 0, 21, 0, 65},
+       {15, 0, 21, 0, 63},
+       {15, 0, 20, 0, 65},
+       {15, 0, 19, 0, 66},
+       {15, 0, 19, 0, 64},
+       {15, 0, 18, 0, 66},
+       {15, 0, 18, 0, 64},
+       {15, 0, 17, 0, 66},
+       {15, 0, 17, 0, 64},
+       {15, 0, 16, 0, 66},
+       {15, 0, 16, 0, 64},
+       {15, 0, 16, 0, 62},
+       {15, 0, 16, 0, 61},
+       {15, 0, 16, 0, 59},
+       {15, 0, 15, 0, 61},
+       {15, 0, 15, 0, 59},
+       {15, 0, 14, 0, 62},
+       {15, 0, 14, 0, 60},
+       {15, 0, 14, 0, 58},
+       {15, 0, 13, 0, 61},
+       {15, 0, 13, 0, 59},
+       {15, 0, 12, 0, 62},
+       {15, 0, 12, 0, 61},
+       {15, 0, 12, 0, 59},
+       {15, 0, 11, 0, 62},
+       {15, 0, 11, 0, 61},
+       {15, 0, 11, 0, 59},
+       {15, 0, 11, 0, 57},
+       {15, 0, 10, 0, 61},
+       {15, 0, 10, 0, 59},
+       {15, 0, 10, 0, 58},
+       {15, 0, 9, 0, 62},
+       {15, 0, 9, 0, 61},
+       {15, 0, 9, 0, 59},
+       {15, 0, 9, 0, 57},
+       {15, 0, 8, 0, 62},
+       {15, 0, 8, 0, 61},
+       {15, 0, 8, 0, 59},
+       {15, 0, 8, 0, 57},
+       {15, 0, 8, 0, 56},
+       {15, 0, 8, 0, 54},
+       {15, 0, 8, 0, 53},
+       {15, 0, 8, 0, 51},
+       {15, 0, 8, 0, 50},
+       {7, 0, 7, 0, 69},
+       {7, 0, 7, 0, 67},
+       {7, 0, 7, 0, 65},
+       {7, 0, 7, 0, 64},
+       {7, 0, 7, 0, 62},
        {7, 0, 7, 0, 60},
        {7, 0, 7, 0, 58},
-       {7, 0, 7, 0, 56},
+       {7, 0, 7, 0, 57},
        {7, 0, 7, 0, 55},
        {7, 0, 6, 0, 62},
-       {7, 0, 6, 0, 60},
-       {7, 0, 6, 0, 58},
+       {7, 0, 6, 0, 61},
+       {7, 0, 6, 0, 59},
        {7, 0, 6, 0, 57},
-       {7, 0, 6, 0, 55},
+       {7, 0, 6, 0, 56},
        {7, 0, 6, 0, 54},
-       {7, 0, 6, 0, 52},
+       {7, 0, 6, 0, 53},
        {7, 0, 5, 0, 61},
-       {7, 0, 5, 0, 59},
-       {7, 0, 5, 0, 57},
+       {7, 0, 5, 0, 60},
+       {7, 0, 5, 0, 58},
        {7, 0, 5, 0, 56},
-       {7, 0, 5, 0, 54},
+       {7, 0, 5, 0, 55},
        {7, 0, 5, 0, 53},
-       {7, 0, 5, 0, 51},
-       {7, 0, 4, 0, 62},
-       {7, 0, 4, 0, 60},
-       {7, 0, 4, 0, 58},
+       {7, 0, 5, 0, 52},
+       {7, 0, 5, 0, 50},
+       {7, 0, 5, 0, 49},
+       {7, 0, 5, 0, 47},
        {7, 0, 4, 0, 57},
-       {7, 0, 4, 0, 55},
+       {7, 0, 4, 0, 56},
        {7, 0, 4, 0, 54},
-       {7, 0, 4, 0, 52},
+       {7, 0, 4, 0, 53},
        {7, 0, 4, 0, 51},
-       {7, 0, 4, 0, 49},
+       {7, 0, 4, 0, 50},
        {7, 0, 4, 0, 48},
+       {7, 0, 4, 0, 47},
        {7, 0, 4, 0, 46},
-       {7, 0, 3, 0, 60},
-       {7, 0, 3, 0, 58},
-       {7, 0, 3, 0, 57},
-       {7, 0, 3, 0, 55},
-       {7, 0, 3, 0, 54},
-       {7, 0, 3, 0, 52},
+       {7, 0, 4, 0, 44},
+       {7, 0, 4, 0, 43},
+       {7, 0, 4, 0, 42},
+       {7, 0, 4, 0, 41},
+       {7, 0, 4, 0, 40},
        {7, 0, 3, 0, 51},
-       {7, 0, 3, 0, 49},
+       {7, 0, 3, 0, 50},
        {7, 0, 3, 0, 48},
+       {7, 0, 3, 0, 47},
        {7, 0, 3, 0, 46},
-       {7, 0, 3, 0, 45},
        {7, 0, 3, 0, 44},
        {7, 0, 3, 0, 43},
+       {7, 0, 3, 0, 42},
        {7, 0, 3, 0, 41},
-       {7, 0, 2, 0, 61},
-       {7, 0, 2, 0, 59},
-       {7, 0, 2, 0, 57},
-       {7, 0, 2, 0, 56},
-       {7, 0, 2, 0, 54},
-       {7, 0, 2, 0, 53},
-       {7, 0, 2, 0, 51},
-       {7, 0, 2, 0, 50},
-       {7, 0, 2, 0, 48},
-       {7, 0, 2, 0, 47},
-       {7, 0, 2, 0, 46},
-       {7, 0, 2, 0, 44},
-       {7, 0, 2, 0, 43},
-       {7, 0, 2, 0, 42},
-       {7, 0, 2, 0, 41},
-       {7, 0, 2, 0, 39},
-       {7, 0, 2, 0, 38},
-       {7, 0, 2, 0, 37},
-       {7, 0, 2, 0, 36},
-       {7, 0, 2, 0, 35},
-       {7, 0, 2, 0, 34},
-       {7, 0, 2, 0, 33},
-       {7, 0, 2, 0, 32},
-       {7, 0, 1, 0, 63},
-       {7, 0, 1, 0, 61},
-       {7, 0, 1, 0, 59},
-       {7, 0, 1, 0, 57},
+       {3, 0, 3, 0, 56},
+       {3, 0, 3, 0, 54},
+       {3, 0, 3, 0, 53},
+       {3, 0, 3, 0, 51},
+       {3, 0, 3, 0, 50},
+       {3, 0, 3, 0, 48},
+       {3, 0, 3, 0, 47},
+       {3, 0, 3, 0, 46},
+       {3, 0, 3, 0, 44},
+       {3, 0, 3, 0, 43},
+       {3, 0, 3, 0, 42},
+       {3, 0, 3, 0, 41},
+       {3, 0, 3, 0, 39},
+       {3, 0, 3, 0, 38},
+       {3, 0, 3, 0, 37},
+       {3, 0, 3, 0, 36},
+       {3, 0, 3, 0, 35},
+       {3, 0, 3, 0, 34},
 };
 
 const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = {
index 5f75e16..489422a 100644 (file)
@@ -20,6 +20,7 @@
 extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[];
 extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0;
 extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
 extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa;
 extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo;
 extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
index 7afc613..48086e8 100644 (file)
@@ -832,7 +832,7 @@ struct wsm_tx {
        /* the MSDU shall be terminated. Overrides the global */
        /* dot11MaxTransmitMsduLifeTime setting [optional] */
        /* Device will set the default value if this is 0. */
-       u32 expire_time;
+       __le32 expire_time;
 
        /* WSM_HT_TX_... */
        __le32 ht_tx_parameters;
index 15f0fad..e4f56ad 100644 (file)
@@ -667,7 +667,7 @@ static int prism2_open(struct net_device *dev)
        if (local->no_pri) {
                printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
                       "f/w\n", dev->name);
-               return 1;
+               return -ENODEV;
        }
 
        if ((local->func->card_present && !local->func->card_present(local)) ||
@@ -682,7 +682,7 @@ static int prism2_open(struct net_device *dev)
                printk(KERN_WARNING "%s: could not enable MAC port\n",
                       dev->name);
                prism2_close(dev);
-               return 1;
+               return -ENODEV;
        }
        if (!local->dev_enabled)
                prism2_callback(local, PRISM2_CALLBACK_ENABLE);
index fe31590..aea667b 100644 (file)
@@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index c092033..f09e257 100644 (file)
@@ -475,6 +475,8 @@ il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header)
        }
 }
 
+#define SMALL_PACKET_SIZE 256
+
 static void
 il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                               struct ieee80211_rx_status *stats)
@@ -483,14 +485,13 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt);
        struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt);
        struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt);
-       u16 len = le16_to_cpu(rx_hdr->len);
+       u32 len = le16_to_cpu(rx_hdr->len);
        struct sk_buff *skb;
        __le16 fc = hdr->frame_control;
+       u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order;
 
        /* We received data from the HW, so stop the watchdog */
-       if (unlikely
-           (len + IL39_RX_FRAME_SIZE >
-            PAGE_SIZE << il->hw_params.rx_page_order)) {
+       if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) {
                D_DROP("Corruption detected!\n");
                return;
        }
@@ -506,26 +507,32 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                D_INFO("Woke queues - frame received on passive channel\n");
        }
 
-       skb = dev_alloc_skb(128);
+       skb = dev_alloc_skb(SMALL_PACKET_SIZE);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
                return;
        }
 
        if (!il3945_mod_params.sw_crypto)
-               il_set_decrypted_flag(il, (struct ieee80211_hdr *)rxb_addr(rxb),
+               il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt,
                                      le32_to_cpu(rx_end->status), stats);
 
-       skb_add_rx_frag(skb, 0, rxb->page,
-                       (void *)rx_hdr->payload - (void *)pkt, len,
-                       len);
-
+       /* If frame is small enough to fit into skb->head, copy it
+        * and do not consume a full page
+        */
+       if (len <= SMALL_PACKET_SIZE) {
+               memcpy(skb_put(skb, len), rx_hdr->payload, len);
+       } else {
+               skb_add_rx_frag(skb, 0, rxb->page,
+                               (void *)rx_hdr->payload - (void *)pkt, len,
+                               fraglen);
+               il->alloc_rxb_page--;
+               rxb->page = NULL;
+       }
        il_update_stats(il, false, fc, len);
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
        ieee80211_rx(il->hw, skb);
-       il->alloc_rxb_page--;
-       rxb->page = NULL;
 }
 
 #define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
index 7acf5ee..5ab50a5 100644 (file)
@@ -574,9 +574,11 @@ il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in)
        return decrypt_out;
 }
 
+#define SMALL_PACKET_SIZE 256
+
 static void
 il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
-                              u16 len, u32 ampdu_status, struct il_rx_buf *rxb,
+                              u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
                               struct ieee80211_rx_status *stats)
 {
        struct sk_buff *skb;
@@ -598,21 +600,25 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
                return;
 
-       skb = dev_alloc_skb(128);
+       skb = dev_alloc_skb(SMALL_PACKET_SIZE);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
                return;
        }
 
-       skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len,
-                       len);
+       if (len <= SMALL_PACKET_SIZE) {
+               memcpy(skb_put(skb, len), hdr, len);
+       } else {
+               skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
+                               len, PAGE_SIZE << il->hw_params.rx_page_order);
+               il->alloc_rxb_page--;
+               rxb->page = NULL;
+       }
 
        il_update_stats(il, false, fc, len);
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
        ieee80211_rx(il->hw, skb);
-       il->alloc_rxb_page--;
-       rxb->page = NULL;
 }
 
 /* Called for N_RX (legacy ABG frames), or
index ed3c42a..3ccbaf7 100644 (file)
@@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta)
  */
 static void
 il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+                        struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *il_sta)
 {
 }
index cbaa5c2..3eb2102 100644 (file)
@@ -22,6 +22,8 @@ config IWLWIFI
                Intel Wireless WiFi Link 6150BGN 2 Adapter
                Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN)
                Intel 2000 Series Wi-Fi Adapters
+               Intel 7260 Wi-Fi Adapter
+               Intel 3160 Wi-Fi Adapter
 
 
          This driver uses the kernel's mac80211 subsystem.
@@ -46,17 +48,16 @@ config IWLDVM
        depends on IWLWIFI
        default IWLWIFI
        help
-         This is the driver supporting the DVM firmware which is
-         currently the only firmware available for existing devices.
+         This is the driver that supports the DVM firmware which is
+         used by most existing devices (with the exception of 7260
+         and 3160).
 
 config IWLMVM
        tristate "Intel Wireless WiFi MVM Firmware support"
        depends on IWLWIFI
        help
-         This is the driver supporting the MVM firmware which is
-         currently only available for 7000 series devices.
-
-         Say yes if you have such a device.
+         This is the driver that supports the MVM firmware which is
+         currently only available for 7260 and 3160 devices.
 
 # don't call it _MODULE -- will confuse Kconfig/fixdep/...
 config IWLWIFI_OPMODE_MODULAR
@@ -127,20 +128,3 @@ config IWLWIFI_DEVICE_TRACING
          If unsure, say Y so we can help you better when problems
          occur.
 endmenu
-
-config IWLWIFI_P2P
-       def_bool y
-       bool "iwlwifi experimental P2P support"
-       depends on IWLWIFI
-       help
-         This option enables experimental P2P support for some devices
-         based on microcode support. Since P2P support is still under
-         development, this option may even enable it for some devices
-         now that turn out to not support it in the future due to
-         microcode restrictions.
-
-         To determine if your microcode supports the experimental P2P
-         offered by this option, check if the driver advertises AP
-         support when it is loaded.
-
-         Say Y only if you want to experiment with P2P.
index 1835511..f2a86ff 100644 (file)
@@ -106,7 +106,6 @@ extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
 #define STATUS_CHANNEL_SWITCH_PENDING 11
 #define STATUS_SCAN_COMPLETE   12
 #define STATUS_POWER_PMI       13
-#define STATUS_SCAN_ROC_EXPIRED 14
 
 struct iwl_ucode_capabilities;
 
@@ -250,7 +249,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid);
 
 /* scan */
 void iwlagn_post_scan(struct iwl_priv *priv);
-void iwlagn_disable_roc(struct iwl_priv *priv);
 int iwl_force_rf_reset(struct iwl_priv *priv, bool external);
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
@@ -265,10 +263,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
                                   enum iwl_scan_type scan_type,
                                   enum ieee80211_band band);
 
-void iwl_scan_roc_expired(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb_status(struct iwl_priv *priv);
-
 /* For faster active scanning, scan will move to the next channel if fewer than
  * PLCP_QUIET_THRESH packets are heard on this channel within
  * ACTIVE_QUIET_TIME after sending probe request.  This shortens the dwell
index d532948..d94f8ab 100644 (file)
 } while (0)
 
 /* file operation */
-#define DEBUGFS_READ_FUNC(name)                                         \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file,               \
-                                       char __user *user_buf,          \
-                                       size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name)                                        \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file,              \
-                                       const char __user *user_buf,    \
-                                       size_t count, loff_t *ppos);
-
-
 #define DEBUGFS_READ_FILE_OPS(name)                                     \
-       DEBUGFS_READ_FUNC(name);                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .read = iwl_dbgfs_##name##_read,                                \
        .open = simple_open,                                            \
@@ -89,7 +77,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 };
 
 #define DEBUGFS_WRITE_FILE_OPS(name)                                    \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .open = simple_open,                                            \
@@ -98,8 +85,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 
 
 #define DEBUGFS_READ_WRITE_FILE_OPS(name)                               \
-       DEBUGFS_READ_FUNC(name);                                        \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
index 60a4e0d..a79fdd1 100644 (file)
@@ -540,7 +540,6 @@ struct iwl_rxon_context {
 enum iwl_scan_type {
        IWL_SCAN_NORMAL,
        IWL_SCAN_RADIO_RESET,
-       IWL_SCAN_ROC,
 };
 
 /**
@@ -825,12 +824,6 @@ struct iwl_priv {
        struct reply_tx_error_statistics reply_tx_stats;
        struct reply_agg_tx_error_statistics reply_agg_tx_stats;
 
-       /* remain-on-channel offload support */
-       struct ieee80211_channel *hw_roc_channel;
-       struct delayed_work hw_roc_disable_work;
-       int hw_roc_duration;
-       bool hw_roc_setup, hw_roc_start_notified;
-
        /* bt coex */
        u8 bt_enable_flag;
        u8 bt_status;
index 3193872..cae4d31 100644 (file)
@@ -76,29 +76,6 @@ static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = {
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_GO) |
-                        BIT(NL80211_IFTYPE_AP),
-       },
-};
-
-static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = {
-       {
-               .max = 2,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT),
-       },
-};
-
 static const struct ieee80211_iface_combination
 iwlagn_iface_combinations_dualmode[] = {
        { .num_different_channels = 1,
@@ -114,21 +91,6 @@ iwlagn_iface_combinations_dualmode[] = {
        },
 };
 
-static const struct ieee80211_iface_combination
-iwlagn_iface_combinations_p2p[] = {
-       { .num_different_channels = 1,
-         .max_interfaces = 2,
-         .beacon_int_infra_match = true,
-         .limits = iwlagn_p2p_sta_go_limits,
-         .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits),
-       },
-       { .num_different_channels = 1,
-         .max_interfaces = 2,
-         .limits = iwlagn_p2p_2sta_limits,
-         .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits),
-       },
-};
-
 /*
  * Not a mac80211 entry point function, but it fits in with all the
  * other mac80211 functions grouped here.
@@ -186,19 +148,13 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
 
        BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
 
-       if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) {
-               hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p;
-               hw->wiphy->n_iface_combinations =
-                       ARRAY_SIZE(iwlagn_iface_combinations_p2p);
-       } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+       if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
                hw->wiphy->iface_combinations =
                        iwlagn_iface_combinations_dualmode;
                hw->wiphy->n_iface_combinations =
                        ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
        }
 
-       hw->wiphy->max_remain_on_channel_duration = 500;
-
        hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
@@ -1159,126 +1115,6 @@ done:
        IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
-static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
-                                    struct ieee80211_vif *vif,
-                                    struct ieee80211_channel *channel,
-                                    int duration,
-                                    enum ieee80211_roc_type type)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-       struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
-       int err = 0;
-
-       if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
-               return -EOPNOTSUPP;
-
-       if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)))
-               return -EOPNOTSUPP;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-       mutex_lock(&priv->mutex);
-
-       if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-               /* mac80211 should not scan while ROC or ROC while scanning */
-               if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) {
-                       err = -EBUSY;
-                       goto out;
-               }
-
-               iwl_scan_cancel_timeout(priv, 100);
-
-               if (test_bit(STATUS_SCAN_HW, &priv->status)) {
-                       err = -EBUSY;
-                       goto out;
-               }
-       }
-
-       priv->hw_roc_channel = channel;
-       /* convert from ms to TU */
-       priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024);
-       priv->hw_roc_start_notified = false;
-       cancel_delayed_work(&priv->hw_roc_disable_work);
-
-       if (!ctx->is_active) {
-               static const struct iwl_qos_info default_qos_data = {
-                       .def_qos_parm = {
-                               .ac[0] = {
-                                       .cw_min = cpu_to_le16(3),
-                                       .cw_max = cpu_to_le16(7),
-                                       .aifsn = 2,
-                                       .edca_txop = cpu_to_le16(1504),
-                               },
-                               .ac[1] = {
-                                       .cw_min = cpu_to_le16(7),
-                                       .cw_max = cpu_to_le16(15),
-                                       .aifsn = 2,
-                                       .edca_txop = cpu_to_le16(3008),
-                               },
-                               .ac[2] = {
-                                       .cw_min = cpu_to_le16(15),
-                                       .cw_max = cpu_to_le16(1023),
-                                       .aifsn = 3,
-                               },
-                               .ac[3] = {
-                                       .cw_min = cpu_to_le16(15),
-                                       .cw_max = cpu_to_le16(1023),
-                                       .aifsn = 7,
-                               },
-                       },
-               };
-
-               ctx->is_active = true;
-               ctx->qos_data = default_qos_data;
-               ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
-               memcpy(ctx->staging.node_addr,
-                      priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
-                      ETH_ALEN);
-               memcpy(ctx->staging.bssid_addr,
-                      priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
-                      ETH_ALEN);
-               err = iwlagn_commit_rxon(priv, ctx);
-               if (err)
-                       goto out;
-               ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK |
-                                            RXON_FILTER_PROMISC_MSK |
-                                            RXON_FILTER_CTL2HOST_MSK;
-
-               err = iwlagn_commit_rxon(priv, ctx);
-               if (err) {
-                       iwlagn_disable_roc(priv);
-                       goto out;
-               }
-               priv->hw_roc_setup = true;
-       }
-
-       err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band);
-       if (err)
-               iwlagn_disable_roc(priv);
-
- out:
-       mutex_unlock(&priv->mutex);
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return err;
-}
-
-static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
-{
-       struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
-       if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
-               return -EOPNOTSUPP;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-       mutex_lock(&priv->mutex);
-       iwl_scan_cancel_timeout(priv, priv->hw_roc_duration);
-       iwlagn_disable_roc(priv);
-       mutex_unlock(&priv->mutex);
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return 0;
-}
-
 static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     enum ieee80211_rssi_event rssi_event)
@@ -1434,12 +1270,8 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
                           viftype, vif->addr);
 
-       cancel_delayed_work_sync(&priv->hw_roc_disable_work);
-
        mutex_lock(&priv->mutex);
 
-       iwlagn_disable_roc(priv);
-
        if (!iwl_is_ready_rf(priv)) {
                IWL_WARN(priv, "Try to add interface when device not ready\n");
                err = -EINVAL;
@@ -1766,8 +1598,6 @@ struct ieee80211_ops iwlagn_hw_ops = {
        .channel_switch = iwlagn_mac_channel_switch,
        .flush = iwlagn_mac_flush,
        .tx_last_beacon = iwlagn_mac_tx_last_beacon,
-       .remain_on_channel = iwlagn_mac_remain_on_channel,
-       .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel,
        .rssi_callback = iwlagn_mac_rssi_callback,
        .set_tim = iwlagn_mac_set_tim,
 };
index 1531a4f..7aad766 100644 (file)
@@ -587,11 +587,6 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
        priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
                BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
 
-       if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P)
-               priv->contexts[IWL_RXON_CTX_PAN].interface_modes |=
-                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                       BIT(NL80211_IFTYPE_P2P_GO);
-
        priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
        priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
        priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
@@ -854,14 +849,6 @@ void iwl_down(struct iwl_priv *priv)
 
        iwl_scan_cancel_timeout(priv, 200);
 
-       /*
-        * If active, scanning won't cancel it, so say it expired.
-        * No race since we hold the mutex here and a new one
-        * can't come in at this time.
-        */
-       if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT)
-               ieee80211_remain_on_channel_expired(priv->hw);
-
        exit_pending =
                test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
 
@@ -1002,41 +989,6 @@ static void iwl_bg_restart(struct work_struct *data)
        }
 }
 
-
-
-
-void iwlagn_disable_roc(struct iwl_priv *priv)
-{
-       struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
-
-       lockdep_assert_held(&priv->mutex);
-
-       if (!priv->hw_roc_setup)
-               return;
-
-       ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
-       ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-
-       priv->hw_roc_channel = NULL;
-
-       memset(ctx->staging.node_addr, 0, ETH_ALEN);
-
-       iwlagn_commit_rxon(priv, ctx);
-
-       ctx->is_active = false;
-       priv->hw_roc_setup = false;
-}
-
-static void iwlagn_disable_roc_work(struct work_struct *work)
-{
-       struct iwl_priv *priv = container_of(work, struct iwl_priv,
-                                            hw_roc_disable_work.work);
-
-       mutex_lock(&priv->mutex);
-       iwlagn_disable_roc(priv);
-       mutex_unlock(&priv->mutex);
-}
-
 /*****************************************************************************
  *
  * driver setup and teardown
@@ -1053,8 +1005,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
        INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
        INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
-       INIT_DELAYED_WORK(&priv->hw_roc_disable_work,
-                         iwlagn_disable_roc_work);
 
        iwl_setup_scan_deferred_work(priv);
 
@@ -1082,7 +1032,6 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv)
 
        cancel_work_sync(&priv->bt_full_concurrency);
        cancel_work_sync(&priv->bt_runtime_config);
-       cancel_delayed_work_sync(&priv->hw_roc_disable_work);
 
        del_timer_sync(&priv->statistics_periodic);
        del_timer_sync(&priv->ucode_trace);
@@ -1169,12 +1118,6 @@ static void iwl_option_config(struct iwl_priv *priv)
 #else
        IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
 #endif
-
-#ifdef CONFIG_IWLWIFI_P2P
-       IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n");
-#else
-       IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n");
-#endif
 }
 
 static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
@@ -1315,10 +1258,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
 
        ucode_flags = fw->ucode_capa.flags;
 
-#ifndef CONFIG_IWLWIFI_P2P
-       ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
-#endif
-
        if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
                priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
                trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
@@ -1413,7 +1352,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
                 * if not PAN, then don't support P2P -- might be a uCode
                 * packaging bug or due to the eeprom check above
                 */
-               ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
                priv->sta_key_max_num = STA_KEY_MAX_NUM;
                trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
 
index 1b69394..b647e50 100644 (file)
@@ -2826,9 +2826,6 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i
 
        lq_sta->flush_timer = 0;
        lq_sta->supp_rates = sta->supp_rates[sband->band];
-       for (j = 0; j < LQ_SIZE; j++)
-               for (i = 0; i < IWL_RATE_COUNT; i++)
-                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
                       sta_id);
@@ -3319,7 +3316,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
-                        struct ieee80211_sta *sta, void *priv_sta)
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *priv_sta)
 {
 }
 static struct rate_control_ops rs_ops = {
index cd1ad00..d7ce2f1 100644 (file)
@@ -564,11 +564,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
        cmd.slots[0].type = 0; /* BSS */
        cmd.slots[1].type = 1; /* PAN */
 
-       if (priv->hw_roc_setup) {
-               /* both contexts must be used for this to happen */
-               slot1 = IWL_MIN_SLOT_TIME;
-               slot0 = 3000;
-       } else if (ctx_bss->vif && ctx_pan->vif) {
+       if (ctx_bss->vif && ctx_pan->vif) {
                int bcnint = ctx_pan->beacon_int;
                int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
 
index 8c686a5..35e0ee8 100644 (file)
@@ -100,9 +100,6 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
                ieee80211_scan_completed(priv->hw, aborted);
        }
 
-       if (priv->scan_type == IWL_SCAN_ROC)
-               iwl_scan_roc_expired(priv);
-
        priv->scan_type = IWL_SCAN_NORMAL;
        priv->scan_vif = NULL;
        priv->scan_request = NULL;
@@ -130,9 +127,6 @@ static void iwl_process_scan_complete(struct iwl_priv *priv)
                goto out_settings;
        }
 
-       if (priv->scan_type == IWL_SCAN_ROC)
-               iwl_scan_roc_expired(priv);
-
        if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
                int err;
 
@@ -284,12 +278,6 @@ static int iwl_rx_scan_start_notif(struct iwl_priv *priv,
                       le32_to_cpu(notif->tsf_low),
                       notif->status, notif->beacon_timer);
 
-       if (priv->scan_type == IWL_SCAN_ROC &&
-           !priv->hw_roc_start_notified) {
-               ieee80211_ready_on_channel(priv->hw);
-               priv->hw_roc_start_notified = true;
-       }
-
        return 0;
 }
 
@@ -697,8 +685,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
        scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
 
-       if (priv->scan_type != IWL_SCAN_ROC &&
-           iwl_is_any_associated(priv)) {
+       if (iwl_is_any_associated(priv)) {
                u16 interval = 0;
                u32 extra;
                u32 suspend_time = 100;
@@ -706,9 +693,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 
                IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
                switch (priv->scan_type) {
-               case IWL_SCAN_ROC:
-                       WARN_ON(1);
-                       break;
                case IWL_SCAN_RADIO_RESET:
                        interval = 0;
                        break;
@@ -728,11 +712,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                scan->suspend_time = cpu_to_le32(scan_suspend_time);
                IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
                               scan_suspend_time, interval);
-       } else if (priv->scan_type == IWL_SCAN_ROC) {
-               scan->suspend_time = 0;
-               scan->max_out_time = 0;
-               scan->quiet_time = 0;
-               scan->quiet_plcp_th = 0;
        }
 
        switch (priv->scan_type) {
@@ -774,9 +753,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                } else
                        IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
                break;
-       case IWL_SCAN_ROC:
-               IWL_DEBUG_SCAN(priv, "Start ROC scan.\n");
-               break;
        }
 
        scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
@@ -898,7 +874,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                                        scan_cmd_size - sizeof(*scan));
                break;
        case IWL_SCAN_RADIO_RESET:
-       case IWL_SCAN_ROC:
                /* use bcast addr, will not be transmitted but must be valid */
                cmd_len = iwl_fill_probe_req(
                                        (struct ieee80211_mgmt *)scan->data,
@@ -926,46 +901,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                                is_active, n_probes,
                                (void *)&scan->data[cmd_len]);
                break;
-       case IWL_SCAN_ROC: {
-               struct iwl_scan_channel *scan_ch;
-               int n_chan, i;
-               u16 dwell;
-
-               dwell = iwl_limit_dwell(priv, priv->hw_roc_duration);
-               n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell);
-
-               scan->channel_count = n_chan;
-
-               scan_ch = (void *)&scan->data[cmd_len];
-
-               for (i = 0; i < n_chan; i++) {
-                       scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
-                       scan_ch->channel =
-                               cpu_to_le16(priv->hw_roc_channel->hw_value);
-
-                       if (i == n_chan - 1)
-                               dwell = priv->hw_roc_duration - i * dwell;
-
-                       scan_ch->active_dwell =
-                       scan_ch->passive_dwell = cpu_to_le16(dwell);
-
-                       /* Set txpower levels to defaults */
-                       scan_ch->dsp_atten = 110;
-
-                       /* NOTE: if we were doing 6Mb OFDM for scans we'd use
-                        * power level:
-                        * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
-                        */
-                       if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ)
-                               scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
-                       else
-                               scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
-                       scan_ch++;
-               }
-               }
-
-               break;
        }
 
        if (scan->channel_count == 0) {
@@ -1035,7 +970,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
 
        IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
                        scan_type == IWL_SCAN_NORMAL ? "" :
-                       scan_type == IWL_SCAN_ROC ? "remain-on-channel " :
                        "internal short ");
 
        set_bit(STATUS_SCANNING, &priv->status);
@@ -1149,40 +1083,3 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv)
                mutex_unlock(&priv->mutex);
        }
 }
-
-void iwl_scan_roc_expired(struct iwl_priv *priv)
-{
-       /*
-        * The status bit should be set here, to prevent a race
-        * where the atomic_read returns 1, but before the execution continues
-        * iwl_scan_offchannel_skb_status() checks if the status bit is set
-        */
-       set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
-
-       if (atomic_read(&priv->num_aux_in_flight) == 0) {
-               ieee80211_remain_on_channel_expired(priv->hw);
-               priv->hw_roc_channel = NULL;
-               schedule_delayed_work(&priv->hw_roc_disable_work,
-                                     10 * HZ);
-
-               clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
-       } else {
-               IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n",
-                              atomic_read(&priv->num_aux_in_flight));
-       }
-}
-
-void iwl_scan_offchannel_skb(struct iwl_priv *priv)
-{
-       WARN_ON(!priv->hw_roc_start_notified);
-       atomic_inc(&priv->num_aux_in_flight);
-}
-
-void iwl_scan_offchannel_skb_status(struct iwl_priv *priv)
-{
-       if (atomic_dec_return(&priv->num_aux_in_flight) == 0 &&
-           test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n");
-               iwl_scan_roc_expired(priv);
-       }
-}
index 5ee983f..da442b8 100644 (file)
@@ -87,7 +87,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
                 priv->lib->bt_params->advanced_bt_coexist &&
                 (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
                 ieee80211_is_reassoc_req(fc) ||
-                skb->protocol == cpu_to_be16(ETH_P_PAE)))
+                info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
                tx_flags |= TX_CMD_FLG_IGNORE_BT;
 
 
@@ -478,9 +478,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
        if (sta_priv && sta_priv->client && !is_agg)
                atomic_inc(&sta_priv->pending_frames);
 
-       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-               iwl_scan_offchannel_skb(priv);
-
        return 0;
 
 drop_unlock_sta:
@@ -1158,7 +1155,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
        struct sk_buff *skb;
        struct iwl_rxon_context *ctx;
        bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
-       bool is_offchannel_skb;
 
        tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
                IWLAGN_TX_RES_TID_POS;
@@ -1178,8 +1174,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
 
        __skb_queue_head_init(&skbs);
 
-       is_offchannel_skb = false;
-
        if (tx_resp->frame_count == 1) {
                u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
                next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
@@ -1256,8 +1250,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                        if (!is_agg)
                                iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
 
-                       is_offchannel_skb =
-                               (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);
                        freed++;
                }
 
@@ -1271,14 +1263,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                if (!is_agg && freed != 1)
                        IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
 
-               /*
-                * An offchannel frame can be send only on the AUX queue, where
-                * there is no aggregation (and reordering) so it only is single
-                * skb is expected to be processed.
-                */
-               if (is_offchannel_skb && freed != 1)
-                       IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed);
-
                IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
                                   iwl_get_tx_fail_reason(status), status);
 
@@ -1298,9 +1282,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                ieee80211_tx_status_ni(priv->hw, skb);
        }
 
-       if (is_offchannel_skb)
-               iwl_scan_offchannel_skb_status(priv);
-
        return 0;
 }
 
index 22b7fa5..76e14c0 100644 (file)
@@ -99,6 +99,7 @@ static const struct iwl_base_params iwl7000_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = true,
+       .pcie_l1_allowed = true,
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
@@ -126,6 +127,16 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
        .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
 };
 
+const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
+       .name = "Intel(R) Dual Band Wireless AC 7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+       .high_temp = true,
+};
+
 const struct iwl_cfg iwl7260_2n_cfg = {
        .name = "Intel(R) Dual Band Wireless N 7260",
        .fw_name_pre = IWL7260_FW_PRE,
index 83b9ff6..e4d370b 100644 (file)
@@ -152,6 +152,7 @@ struct iwl_base_params {
        unsigned int wd_timeout;
        u32 max_event_log_size;
        const bool shadow_reg_enable;
+       const bool pcie_l1_allowed;
 };
 
 /*
@@ -205,6 +206,7 @@ struct iwl_eeprom_params {
  * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
  * @rx_with_siso_diversity: 1x1 device with rx antenna diversity
  * @internal_wimax_coex: internal wifi/wimax combo device
+ * @high_temp: Is this NIC is designated to be in high temperature.
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -233,6 +235,7 @@ struct iwl_cfg {
        enum iwl_led_mode led_mode;
        const bool rx_with_siso_diversity;
        const bool internal_wimax_coex;
+       bool high_temp;
 };
 
 /*
@@ -283,6 +286,7 @@ extern const struct iwl_cfg iwl135_bgn_cfg;
 #endif /* CONFIG_IWLDVM */
 #if IS_ENABLED(CONFIG_IWLMVM)
 extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp;
 extern const struct iwl_cfg iwl7260_2n_cfg;
 extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
index 7edb851..b2bb32a 100644 (file)
@@ -145,6 +145,7 @@ do {                                                                \
 #define IWL_DL_RX              0x01000000
 #define IWL_DL_ISR             0x02000000
 #define IWL_DL_HT              0x04000000
+#define IWL_DL_EXTERNAL                0x08000000
 /* 0xF0000000 - 0x10000000 */
 #define IWL_DL_11H             0x10000000
 #define IWL_DL_STATS           0x20000000
@@ -153,6 +154,7 @@ do {                                                                \
 
 #define IWL_DEBUG_INFO(p, f, a...)     IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
 #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
+#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
 #define IWL_DEBUG_TEMP(p, f, a...)     IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
 #define IWL_DEBUG_SCAN(p, f, a...)     IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
 #define IWL_DEBUG_RX(p, f, a...)       IWL_DEBUG(p, IWL_DL_RX, f, ## a)
index 4491c1c..684c416 100644 (file)
 static inline bool iwl_trace_data(struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (void *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
-       if (ieee80211_is_data(hdr->frame_control))
-               return skb->protocol != cpu_to_be16(ETH_P_PAE);
-       return false;
+       if (!ieee80211_is_data(hdr->frame_control))
+               return false;
+       return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO);
 }
 
 static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
index d0162d4..99e1da3 100644 (file)
@@ -843,7 +843,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        int i;
        bool load_module = false;
 
-       fw->ucode_capa.max_probe_length = 200;
+       fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
        fw->ucode_capa.standard_phy_calibration_size =
                        IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
 
@@ -1032,8 +1032,10 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
        int ret;
 
        drv = kzalloc(sizeof(*drv), GFP_KERNEL);
-       if (!drv)
-               return NULL;
+       if (!drv) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
        drv->trans = trans;
        drv->dev = trans->dev;
@@ -1078,7 +1080,7 @@ err_free_dbgfs:
 err_free_drv:
 #endif
        kfree(drv);
-
+err:
        return ERR_PTR(ret);
 }
 
index f844d5c..a122368 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
  * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api
+ * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API.
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ *     (rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
  */
 enum iwl_ucode_tlv_flag {
-       IWL_UCODE_TLV_FLAGS_PAN         = BIT(0),
-       IWL_UCODE_TLV_FLAGS_NEWSCAN     = BIT(1),
-       IWL_UCODE_TLV_FLAGS_MFP         = BIT(2),
-       IWL_UCODE_TLV_FLAGS_P2P         = BIT(3),
-       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
+       IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
+       IWL_UCODE_TLV_FLAGS_NEWSCAN             = BIT(1),
+       IWL_UCODE_TLV_FLAGS_MFP                 = BIT(2),
+       IWL_UCODE_TLV_FLAGS_P2P                 = BIT(3),
+       IWL_UCODE_TLV_FLAGS_DW_BC_TABLE         = BIT(4),
+       IWL_UCODE_TLV_FLAGS_UAPSD               = BIT(6),
+       IWL_UCODE_TLV_FLAGS_RX_ENERGY_API       = BIT(8),
+       IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2   = BIT(9),
+       IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
+       IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -88,6 +99,9 @@ enum iwl_ucode_tlv_flag {
 #define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE                19
 #define IWL_MAX_PHY_CALIBRATE_TBL_SIZE                 253
 
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH   200
+
 /**
  * enum iwl_ucode_type
  *
index 305c81f..dfa4d2e 100644 (file)
@@ -33,6 +33,8 @@
 #include "iwl-io.h"
 #include "iwl-csr.h"
 #include "iwl-debug.h"
+#include "iwl-fh.h"
+#include "iwl-csr.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
@@ -166,3 +168,68 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
        }
 }
 IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
+
+static const char *get_fh_string(int cmd)
+{
+#define IWL_CMD(x) case x: return #x
+       switch (cmd) {
+       IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+       IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+       IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+       IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+       IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+       IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+       IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+       IWL_CMD(FH_TSSR_TX_STATUS_REG);
+       IWL_CMD(FH_TSSR_TX_ERROR_REG);
+       default:
+               return "UNKNOWN";
+       }
+#undef IWL_CMD
+}
+
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
+{
+       int i;
+       static const u32 fh_tbl[] = {
+               FH_RSCSR_CHNL0_STTS_WPTR_REG,
+               FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+               FH_RSCSR_CHNL0_WPTR,
+               FH_MEM_RCSR_CHNL0_CONFIG_REG,
+               FH_MEM_RSSR_SHARED_CTRL_REG,
+               FH_MEM_RSSR_RX_STATUS_REG,
+               FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+               FH_TSSR_TX_STATUS_REG,
+               FH_TSSR_TX_ERROR_REG
+       };
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (buf) {
+               int pos = 0;
+               size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return -ENOMEM;
+
+               pos += scnprintf(*buf + pos, bufsz - pos,
+                               "FH register values:\n");
+
+               for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+                       pos += scnprintf(*buf + pos, bufsz - pos,
+                               "  %34s: 0X%08x\n",
+                               get_fh_string(fh_tbl[i]),
+                               iwl_read_direct32(trans, fh_tbl[i]));
+
+               return pos;
+       }
+#endif
+
+       IWL_ERR(trans, "FH register values:\n");
+       for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
+               IWL_ERR(trans, "  %34s: 0X%08x\n",
+                       get_fh_string(fh_tbl[i]),
+                       iwl_read_direct32(trans, fh_tbl[i]));
+
+       return 0;
+}
index fd9f5b9..63d10ec 100644 (file)
@@ -77,4 +77,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 
+/* Error handling */
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
+
 #endif
index acd2665..b76a9a8 100644 (file)
@@ -118,6 +118,7 @@ static const u8 iwl_nvm_channels[] = {
 #define LAST_2GHZ_HT_PLUS      9
 #define LAST_5GHZ_HT           161
 
+#define DEFAULT_MAX_TX_POWER 16
 
 /* rate data (static) */
 static struct ieee80211_rate iwl_cfg80211_rates[] = {
@@ -232,8 +233,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
                /* Initialize regulatory-based run-time data */
 
-               /* TODO: read the real value from the NVM */
-               channel->max_power = 0;
+               /*
+                * Default value - highest tx power value.  max_power
+                * is not used in mvm, and is used for backwards compatibility
+                */
+               channel->max_power = DEFAULT_MAX_TX_POWER;
                is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
                IWL_DEBUG_EEPROM(dev,
                                 "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
index 98c7aa7..976448a 100644 (file)
@@ -93,7 +93,7 @@ struct iwl_cfg;
  *     1) The driver layer (iwl-drv.c) chooses the op_mode based on the
  *        capabilities advertized by the fw file (in TLV format).
  *     2) The driver layer starts the op_mode (ops->start)
- *     3) The op_mode registers registers mac80211
+ *     3) The op_mode registers mac80211
  *     4) The op_mode is governed by mac80211
  *     5) The driver layer stops the op_mode
  */
@@ -112,7 +112,7 @@ struct iwl_cfg;
  * @stop: stop the op_mode. Must free all the memory allocated.
  *     May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
- *     HCMD the this Rx responds to.
+ *     HCMD this Rx responds to.
  *     This callback may sleep, it is called from a threaded IRQ handler.
  * @queue_full: notifies that a HW queue is full.
  *     Must be atomic and called with BH disabled.
index 8d91422..dd57a36 100644 (file)
@@ -180,7 +180,7 @@ struct iwl_rx_packet {
  * enum CMD_MODE - how to send the host commands ?
  *
  * @CMD_SYNC: The caller will be stalled until the fw responds to the command
- * @CMD_ASYNC: Return right away and don't want for the response
+ * @CMD_ASYNC: Return right away and don't wait for the response
  * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
  *     response. The caller needs to call iwl_free_resp when done.
  */
@@ -218,7 +218,7 @@ struct iwl_device_cmd {
  *
  * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's
  *     ring. The transport layer doesn't map the command's buffer to DMA, but
- *     rather copies it to an previously allocated DMA buffer. This flag tells
+ *     rather copies it to a previously allocated DMA buffer. This flag tells
  *     the transport layer not to copy the command, but to map the existing
  *     buffer (that is passed in) instead. This saves the memcpy and allows
  *     commands that are bigger than the fixed buffer to be submitted.
@@ -243,7 +243,7 @@ enum iwl_hcmd_dataflag {
  * @handler_status: return value of the handler of the command
  *     (put in setup_rx_handlers) - valid for SYNC mode only
  * @flags: can be CMD_*
- * @len: array of the lenths of the chunks in data
+ * @len: array of the lengths of the chunks in data
  * @dataflags: IWL_HCMD_DFL_*
  * @id: id of the host command
  */
@@ -396,8 +396,6 @@ struct iwl_trans;
  *     May sleep
  * @dbgfs_register: add the dbgfs files under this directory. Files will be
  *     automatically deleted.
- * @suspend: stop the device unless WoWLAN is configured
- * @resume: resume activity of the device
  * @write8: write a u8 to a register at offset ofs from the BAR
  * @write32: write a u32 to a register at offset ofs from the BAR
  * @read32: read a u32 register at offset ofs from the BAR
@@ -443,10 +441,7 @@ struct iwl_trans_ops {
 
        int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
        int (*wait_tx_queue_empty)(struct iwl_trans *trans);
-#ifdef CONFIG_PM_SLEEP
-       int (*suspend)(struct iwl_trans *trans);
-       int (*resume)(struct iwl_trans *trans);
-#endif
+
        void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
        void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
        u32 (*read32)(struct iwl_trans *trans, u32 ofs);
@@ -700,18 +695,6 @@ static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
        return trans->ops->dbgfs_register(trans, dir);
 }
 
-#ifdef CONFIG_PM_SLEEP
-static inline int iwl_trans_suspend(struct iwl_trans *trans)
-{
-       return trans->ops->suspend(trans);
-}
-
-static inline int iwl_trans_resume(struct iwl_trans *trans)
-{
-       return trans->ops->resume(trans);
-}
-#endif
-
 static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val)
 {
        trans->ops->write8(trans, ofs, val);
index ff856e5..6d73817 100644 (file)
@@ -2,7 +2,7 @@ obj-$(CONFIG_IWLMVM)   += iwlmvm.o
 iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
 iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
 iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o bt-coex.o
+iwlmvm-y += power.o power_legacy.o bt-coex.o
 iwlmvm-y += led.o tt.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
index dbd622a..0fad98b 100644 (file)
@@ -220,66 +220,87 @@ static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = {
 
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
 {
-       struct iwl_bt_coex_cmd cmd = {
-               .max_kill = 5,
-               .bt3_time_t7_value = 1,
-               .bt3_prio_sample_time = 2,
-               .bt3_timer_t2_value = 0xc,
+       struct iwl_bt_coex_cmd *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+               .flags = CMD_SYNC,
        };
        int ret;
 
-       cmd.flags = iwlwifi_mod_params.bt_coex_active ?
+       /* go to CALIB state in internal BT-Coex state machine */
+       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
+                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
+                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
+       if (ret)
+               return ret;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->max_kill = 5;
+       bt_cmd->bt3_time_t7_value = 1;
+       bt_cmd->bt3_prio_sample_time = 2;
+       bt_cmd->bt3_timer_t2_value = 0xc;
+
+       bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
-       cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+       bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
 
-       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
-                                       BT_VALID_BT_PRIO_BOOST |
-                                       BT_VALID_MAX_KILL |
-                                       BT_VALID_3W_TMRS |
-                                       BT_VALID_KILL_ACK |
-                                       BT_VALID_KILL_CTS |
-                                       BT_VALID_REDUCED_TX_POWER |
-                                       BT_VALID_LUT);
+       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE |
+                                           BT_VALID_BT_PRIO_BOOST |
+                                           BT_VALID_MAX_KILL |
+                                           BT_VALID_3W_TMRS |
+                                           BT_VALID_KILL_ACK |
+                                           BT_VALID_KILL_CTS |
+                                           BT_VALID_REDUCED_TX_POWER |
+                                           BT_VALID_LUT);
 
        if (mvm->cfg->bt_shared_single_ant)
-               memcpy(&cmd.decision_lut, iwl_single_shared_ant_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup,
                       sizeof(iwl_single_shared_ant_lookup));
        else if (is_loose_coex())
-               memcpy(&cmd.decision_lut, iwl_loose_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_loose_lookup,
                       sizeof(iwl_tight_lookup));
        else
-               memcpy(&cmd.decision_lut, iwl_tight_lookup,
+               memcpy(&bt_cmd->decision_lut, iwl_tight_lookup,
                       sizeof(iwl_tight_lookup));
 
-       cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
-       cmd.kill_ack_msk =
+       bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
+       bt_cmd->kill_ack_msk =
                cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
-       cmd.kill_cts_msk =
+       bt_cmd->kill_cts_msk =
                cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
 
-       /* go to CALIB state in internal BT-Coex state machine */
-       ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN,
-                             BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
-
-       ret  = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE,
-                              BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
-       if (ret)
-               return ret;
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+       kfree(bt_cmd);
+       return ret;
 }
 
 static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                                           bool reduced_tx_power)
 {
        enum iwl_bt_kill_msk bt_kill_msk;
-       struct iwl_bt_coex_cmd cmd = {};
+       struct iwl_bt_coex_cmd *bt_cmd;
        struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .data[0] = &bt_cmd,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+               .flags = CMD_SYNC,
+       };
+       int ret = 0;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -308,24 +329,40 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                return 0;
 
        mvm->bt_kill_msk = bt_kill_msk;
-       cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
-       cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
-       cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+       bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+       bt_cmd->valid_bit_msk =
+               cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
 
        IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk);
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
 }
 
 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
                                       bool enable)
 {
-       struct iwl_bt_coex_cmd cmd = {
-               .valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
-               .bt_reduced_tx_power = sta_id,
+       struct iwl_bt_coex_cmd *bt_cmd;
+       /* Send ASYNC since this can be sent from an atomic context */
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_DUP, },
+               .flags = CMD_ASYNC,
        };
+
        struct ieee80211_sta *sta;
        struct iwl_mvm_sta *mvmsta;
+       int ret;
 
        /* This can happen if the station has been removed right now */
        if (sta_id == IWL_MVM_STATION_COUNT)
@@ -339,17 +376,26 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        if (mvmsta->bt_reduced_txpower == enable)
                return 0;
 
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
+       bt_cmd->bt_reduced_tx_power = sta_id;
+
        if (enable)
-               cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+               bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
 
        IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
                       enable ? "en" : "dis", sta_id);
 
        mvmsta->bt_reduced_txpower = enable;
 
-       /* Send ASYNC since this can be sent from an atomic context */
-       return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
-                                   sizeof(cmd), &cmd);
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
 }
 
 struct iwl_bt_iterator_data {
@@ -384,6 +430,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
 
        smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       /* non associated BSSes aren't to be considered */
+       if (!vif->bss_conf.assoc)
+               return;
+
        if (band != IEEE80211_BAND_2GHZ) {
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
                                    smps_mode);
@@ -523,6 +573,8 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
                                        lockdep_is_held(&mvm->mutex));
        mvmsta = (void *)sta->drv_priv;
 
+       data->num_bss_ifaces++;
+
        /*
         * This interface doesn't support reduced Tx power (because of low
         * RSSI probably), then set bt_kill_msk to default values.
@@ -588,23 +640,5 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
 void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       enum ieee80211_band band;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(vif->chanctx_conf);
-       if (chanctx_conf && chanctx_conf->def.chan)
-               band = chanctx_conf->def.chan->band;
-       else
-               band = -1;
-       rcu_read_unlock();
-
-       /* if we are in 2GHz we will get a notification from the fw */
-       if (band == IEEE80211_BAND_2GHZ)
-               return;
-
-       /* else, we can remove all the constraints */
-       memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
-
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
new file mode 100644 (file)
index 0000000..2bf29f7
--- /dev/null
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#ifndef __MVM_CONSTANTS_H
+#define __MVM_CONSTANTS_H
+
+#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
+#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT     (100 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT      (10 * USEC_PER_MSEC)
+#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
+#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT          (50 * USEC_PER_MSEC)
+#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS       20
+#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS       20
+#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT       50
+#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT       50
+#define IWL_MVM_PS_SNOOZE_INTERVAL             25
+#define IWL_MVM_PS_SNOOZE_WINDOW               50
+#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW                25
+
+#endif /* __MVM_CONSTANTS_H */
index 83da884..417639f 100644 (file)
@@ -105,7 +105,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
        list_for_each_entry(ifa, &idev->addr_list, if_list) {
                mvmvif->target_ipv6_addrs[idx] = ifa->addr;
                idx++;
-               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
+               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
                        break;
        }
        read_unlock_bh(&idev->lock);
@@ -378,36 +378,68 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
 static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif)
 {
-       struct iwl_proto_offload_cmd cmd = {};
+       union {
+               struct iwl_proto_offload_cmd_v1 v1;
+               struct iwl_proto_offload_cmd_v2 v2;
+       } cmd = {};
+       struct iwl_proto_offload_cmd_common *common;
+       u32 enabled = 0, size;
 #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
 
-       if (mvmvif->num_target_ipv6_addrs) {
-               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
-               memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
-       }
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+               if (mvmvif->num_target_ipv6_addrs) {
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+                       memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
+               }
+
+               BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
+                            sizeof(mvmvif->target_ipv6_addrs[0]));
 
-       BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
-                    sizeof(mvmvif->target_ipv6_addrs[i]));
+               for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+                                   IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
+                       memcpy(cmd.v2.target_ipv6_addr[i],
+                              &mvmvif->target_ipv6_addrs[i],
+                              sizeof(cmd.v2.target_ipv6_addr[i]));
+       } else {
+               if (mvmvif->num_target_ipv6_addrs) {
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+                       memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
+               }
 
-       for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
-               memcpy(cmd.target_ipv6_addr[i],
-                      &mvmvif->target_ipv6_addrs[i],
-                      sizeof(cmd.target_ipv6_addr[i]));
+               BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
+                            sizeof(mvmvif->target_ipv6_addrs[0]));
+
+               for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
+                                   IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
+                       memcpy(cmd.v1.target_ipv6_addr[i],
+                              &mvmvif->target_ipv6_addrs[i],
+                              sizeof(cmd.v1.target_ipv6_addr[i]));
+       }
 #endif
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+               common = &cmd.v2.common;
+               size = sizeof(cmd.v2);
+       } else {
+               common = &cmd.v1.common;
+               size = sizeof(cmd.v1);
+       }
+
        if (vif->bss_conf.arp_addr_cnt) {
-               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
-               cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
-               memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
+               enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
+               common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+               memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
        }
 
-       if (!cmd.enabled)
+       if (!enabled)
                return 0;
 
+       common->enabled = cpu_to_le32(enabled);
+
        return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
+                                   size, &cmd);
 }
 
 enum iwl_mvm_tcp_packet_type {
@@ -1077,73 +1109,16 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
        return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
-static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
-                                        struct ieee80211_vif *vif)
+static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_wowlan_status *status)
 {
-       u32 base = mvm->error_event_table;
-       struct error_table_start {
-               /* cf. struct iwl_error_event_table */
-               u32 valid;
-               u32 error_id;
-       } err_info;
+       struct sk_buff *pkt = NULL;
        struct cfg80211_wowlan_wakeup wakeup = {
                .pattern_idx = -1,
        };
        struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
-       struct iwl_host_cmd cmd = {
-               .id = WOWLAN_GET_STATUSES,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
-       };
-       struct iwl_wowlan_status *status;
-       u32 reasons;
-       int ret, len;
-       struct sk_buff *pkt = NULL;
-
-       iwl_trans_read_mem_bytes(mvm->trans, base,
-                                &err_info, sizeof(err_info));
-
-       if (err_info.valid) {
-               IWL_INFO(mvm, "error table is valid (%d)\n",
-                        err_info.valid);
-               if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
-                       wakeup.rfkill_release = true;
-                       ieee80211_report_wowlan_wakeup(vif, &wakeup,
-                                                      GFP_KERNEL);
-               }
-               return;
-       }
-
-       /* only for tracing for now */
-       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
-       if (ret)
-               IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
-
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
-       if (ret) {
-               IWL_ERR(mvm, "failed to query status (%d)\n", ret);
-               return;
-       }
-
-       /* RF-kill already asserted again... */
-       if (!cmd.resp_pkt)
-               return;
-
-       len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-       if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) {
-               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-               goto out;
-       }
-
-       status = (void *)cmd.resp_pkt->data;
-
-       if (len - sizeof(struct iwl_cmd_header) !=
-           sizeof(*status) +
-           ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
-               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
-               goto out;
-       }
-
-       reasons = le32_to_cpu(status->wakeup_reasons);
+       u32 reasons = le32_to_cpu(status->wakeup_reasons);
 
        if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
                wakeup_report = NULL;
@@ -1206,6 +1181,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                        pktsize -= hdrlen;
 
                        if (ieee80211_has_protected(hdr->frame_control)) {
+                               /*
+                                * This is unlocked and using gtk_i(c)vlen,
+                                * but since everything is under RTNL still
+                                * that's not really a problem - changing
+                                * it would be difficult.
+                                */
                                if (is_multicast_ether_addr(hdr->addr1)) {
                                        ivlen = mvm->gtk_ivlen;
                                        icvlen += mvm->gtk_icvlen;
@@ -1256,9 +1237,82 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
  report:
        ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
        kfree_skb(pkt);
+}
 
- out:
+/* releases the MVM mutex */
+static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif)
+{
+       u32 base = mvm->error_event_table;
+       struct error_table_start {
+               /* cf. struct iwl_error_event_table */
+               u32 valid;
+               u32 error_id;
+       } err_info;
+       struct iwl_host_cmd cmd = {
+               .id = WOWLAN_GET_STATUSES,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+       };
+       struct iwl_wowlan_status *status;
+       int ret, len;
+
+       iwl_trans_read_mem_bytes(mvm->trans, base,
+                                &err_info, sizeof(err_info));
+
+       if (err_info.valid) {
+               IWL_INFO(mvm, "error table is valid (%d)\n",
+                        err_info.valid);
+               if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
+                       struct cfg80211_wowlan_wakeup wakeup = {
+                               .rfkill_release = true,
+                       };
+                       ieee80211_report_wowlan_wakeup(vif, &wakeup,
+                                                      GFP_KERNEL);
+               }
+               goto out_unlock;
+       }
+
+       /* only for tracing for now */
+       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+       if (ret)
+               IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret) {
+               IWL_ERR(mvm, "failed to query status (%d)\n", ret);
+               goto out_unlock;
+       }
+
+       /* RF-kill already asserted again... */
+       if (!cmd.resp_pkt)
+               goto out_unlock;
+
+       len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+       if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) {
+               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+               goto out_free_resp;
+       }
+
+       status = (void *)cmd.resp_pkt->data;
+
+       if (len - sizeof(struct iwl_cmd_header) !=
+           sizeof(*status) +
+           ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
+               IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+               goto out_free_resp;
+       }
+
+       /* now we have all the data we need, unlock to avoid mac80211 issues */
+       mutex_unlock(&mvm->mutex);
+
+       iwl_mvm_report_wakeup_reasons(mvm, vif, status);
+       iwl_free_resp(&cmd);
+       return;
+
+ out_free_resp:
        iwl_free_resp(&cmd);
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
 }
 
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
@@ -1315,10 +1369,13 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
        iwl_mvm_read_d3_sram(mvm);
 
        iwl_mvm_query_wakeup_reasons(mvm, vif);
+       /* has unlocked the mutex, so skip that */
+       goto out;
 
  out_unlock:
        mutex_unlock(&mvm->mutex);
 
+ out:
        if (!test && vif)
                ieee80211_resume_disconnect(vif);
 
index c24a744..aac81b8 100644 (file)
@@ -352,6 +352,10 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
                IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
                dbgfs_pm->lprx_rssi_threshold = val;
                break;
+       case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
+               IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
+               dbgfs_pm->snooze_ena = val;
+               break;
        }
 }
 
@@ -405,6 +409,10 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file,
                    POWER_LPRX_RSSI_THRESHOLD_MIN)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
+       } else if (!strncmp("snooze_enable=", buf, 14)) {
+               if (sscanf(buf + 14, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
        } else {
                return -EINVAL;
        }
@@ -424,40 +432,11 @@ static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
        struct ieee80211_vif *vif = file->private_data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm *mvm = mvmvif->dbgfs_data;
-       struct iwl_powertable_cmd cmd = {};
-       char buf[256];
+       char buf[512];
        int bufsz = sizeof(buf);
-       int pos = 0;
+       int pos;
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
-
-       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
-                        (cmd.flags &
-                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
-                        0 : 1);
-       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
-                        le32_to_cpu(cmd.skip_dtim_periods));
-       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
-                        iwlmvm_mod_params.power_scheme);
-       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
-                        le16_to_cpu(cmd.flags));
-       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
-                        cmd.keep_alive_seconds);
-
-       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
-                                (cmd.flags &
-                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
-                                1 : 0);
-               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.rx_data_timeout));
-               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
-                                le32_to_cpu(cmd.tx_data_timeout));
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "lprx_rssi_threshold = %d\n",
-                                        le32_to_cpu(cmd.lprx_rssi_threshold));
-       }
+       pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -621,25 +600,160 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
 }
 #undef BT_MBOX_PRINT
 
+#define PRINT_STATS_LE32(_str, _val)                                   \
+                        pos += scnprintf(buf + pos, bufsz - pos,       \
+                                         fmt_table, _str,              \
+                                         le32_to_cpu(_val))
+
+static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
+                                         char __user *user_buf, size_t count,
+                                         loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       static const char *fmt_table = "\t%-30s %10u\n";
+       static const char *fmt_header = "%-32s\n";
+       int pos = 0;
+       char *buf;
+       int ret;
+       int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 +
+                   sizeof(struct mvm_statistics_rx_non_phy) * 10 +
+                   sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200;
+       struct mvm_statistics_rx_phy *ofdm;
+       struct mvm_statistics_rx_phy *cck;
+       struct mvm_statistics_rx_non_phy *general;
+       struct mvm_statistics_rx_ht_phy *ht;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       ofdm = &mvm->rx_stats.ofdm;
+       cck = &mvm->rx_stats.cck;
+       general = &mvm->rx_stats.general;
+       ht = &mvm->rx_stats.ofdm_ht;
+
+       pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+                        "Statistics_Rx - OFDM");
+       PRINT_STATS_LE32("ina_cnt", ofdm->ina_cnt);
+       PRINT_STATS_LE32("fina_cnt", ofdm->fina_cnt);
+       PRINT_STATS_LE32("plcp_err", ofdm->plcp_err);
+       PRINT_STATS_LE32("crc32_err", ofdm->crc32_err);
+       PRINT_STATS_LE32("overrun_err", ofdm->overrun_err);
+       PRINT_STATS_LE32("early_overrun_err", ofdm->early_overrun_err);
+       PRINT_STATS_LE32("crc32_good", ofdm->crc32_good);
+       PRINT_STATS_LE32("false_alarm_cnt", ofdm->false_alarm_cnt);
+       PRINT_STATS_LE32("fina_sync_err_cnt", ofdm->fina_sync_err_cnt);
+       PRINT_STATS_LE32("sfd_timeout", ofdm->sfd_timeout);
+       PRINT_STATS_LE32("fina_timeout", ofdm->fina_timeout);
+       PRINT_STATS_LE32("unresponded_rts", ofdm->unresponded_rts);
+       PRINT_STATS_LE32("rxe_frame_lmt_overrun",
+                        ofdm->rxe_frame_limit_overrun);
+       PRINT_STATS_LE32("sent_ack_cnt", ofdm->sent_ack_cnt);
+       PRINT_STATS_LE32("sent_cts_cnt", ofdm->sent_cts_cnt);
+       PRINT_STATS_LE32("sent_ba_rsp_cnt", ofdm->sent_ba_rsp_cnt);
+       PRINT_STATS_LE32("dsp_self_kill", ofdm->dsp_self_kill);
+       PRINT_STATS_LE32("mh_format_err", ofdm->mh_format_err);
+       PRINT_STATS_LE32("re_acq_main_rssi_sum", ofdm->re_acq_main_rssi_sum);
+       PRINT_STATS_LE32("reserved", ofdm->reserved);
+
+       pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+                        "Statistics_Rx - CCK");
+       PRINT_STATS_LE32("ina_cnt", cck->ina_cnt);
+       PRINT_STATS_LE32("fina_cnt", cck->fina_cnt);
+       PRINT_STATS_LE32("plcp_err", cck->plcp_err);
+       PRINT_STATS_LE32("crc32_err", cck->crc32_err);
+       PRINT_STATS_LE32("overrun_err", cck->overrun_err);
+       PRINT_STATS_LE32("early_overrun_err", cck->early_overrun_err);
+       PRINT_STATS_LE32("crc32_good", cck->crc32_good);
+       PRINT_STATS_LE32("false_alarm_cnt", cck->false_alarm_cnt);
+       PRINT_STATS_LE32("fina_sync_err_cnt", cck->fina_sync_err_cnt);
+       PRINT_STATS_LE32("sfd_timeout", cck->sfd_timeout);
+       PRINT_STATS_LE32("fina_timeout", cck->fina_timeout);
+       PRINT_STATS_LE32("unresponded_rts", cck->unresponded_rts);
+       PRINT_STATS_LE32("rxe_frame_lmt_overrun",
+                        cck->rxe_frame_limit_overrun);
+       PRINT_STATS_LE32("sent_ack_cnt", cck->sent_ack_cnt);
+       PRINT_STATS_LE32("sent_cts_cnt", cck->sent_cts_cnt);
+       PRINT_STATS_LE32("sent_ba_rsp_cnt", cck->sent_ba_rsp_cnt);
+       PRINT_STATS_LE32("dsp_self_kill", cck->dsp_self_kill);
+       PRINT_STATS_LE32("mh_format_err", cck->mh_format_err);
+       PRINT_STATS_LE32("re_acq_main_rssi_sum", cck->re_acq_main_rssi_sum);
+       PRINT_STATS_LE32("reserved", cck->reserved);
+
+       pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+                        "Statistics_Rx - GENERAL");
+       PRINT_STATS_LE32("bogus_cts", general->bogus_cts);
+       PRINT_STATS_LE32("bogus_ack", general->bogus_ack);
+       PRINT_STATS_LE32("non_bssid_frames", general->non_bssid_frames);
+       PRINT_STATS_LE32("filtered_frames", general->filtered_frames);
+       PRINT_STATS_LE32("non_channel_beacons", general->non_channel_beacons);
+       PRINT_STATS_LE32("channel_beacons", general->channel_beacons);
+       PRINT_STATS_LE32("num_missed_bcon", general->num_missed_bcon);
+       PRINT_STATS_LE32("adc_rx_saturation_time",
+                        general->adc_rx_saturation_time);
+       PRINT_STATS_LE32("ina_detection_search_time",
+                        general->ina_detection_search_time);
+       PRINT_STATS_LE32("beacon_silence_rssi_a",
+                        general->beacon_silence_rssi_a);
+       PRINT_STATS_LE32("beacon_silence_rssi_b",
+                        general->beacon_silence_rssi_b);
+       PRINT_STATS_LE32("beacon_silence_rssi_c",
+                        general->beacon_silence_rssi_c);
+       PRINT_STATS_LE32("interference_data_flag",
+                        general->interference_data_flag);
+       PRINT_STATS_LE32("channel_load", general->channel_load);
+       PRINT_STATS_LE32("dsp_false_alarms", general->dsp_false_alarms);
+       PRINT_STATS_LE32("beacon_rssi_a", general->beacon_rssi_a);
+       PRINT_STATS_LE32("beacon_rssi_b", general->beacon_rssi_b);
+       PRINT_STATS_LE32("beacon_rssi_c", general->beacon_rssi_c);
+       PRINT_STATS_LE32("beacon_energy_a", general->beacon_energy_a);
+       PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b);
+       PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c);
+       PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills);
+       PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu);
+
+       pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+                        "Statistics_Rx - HT");
+       PRINT_STATS_LE32("plcp_err", ht->plcp_err);
+       PRINT_STATS_LE32("overrun_err", ht->overrun_err);
+       PRINT_STATS_LE32("early_overrun_err", ht->early_overrun_err);
+       PRINT_STATS_LE32("crc32_good", ht->crc32_good);
+       PRINT_STATS_LE32("crc32_err", ht->crc32_err);
+       PRINT_STATS_LE32("mh_format_err", ht->mh_format_err);
+       PRINT_STATS_LE32("agg_crc32_good", ht->agg_crc32_good);
+       PRINT_STATS_LE32("agg_mpdu_cnt", ht->agg_mpdu_cnt);
+       PRINT_STATS_LE32("agg_cnt", ht->agg_cnt);
+       PRINT_STATS_LE32("unsupport_mcs", ht->unsupport_mcs);
+
+       mutex_unlock(&mvm->mutex);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+
+       return ret;
+}
+#undef PRINT_STAT_LE32
+
 static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
                                          const char __user *user_buf,
                                          size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       bool restart_fw = iwlwifi_mod_params.restart_fw;
        int ret;
 
-       iwlwifi_mod_params.restart_fw = true;
-
        mutex_lock(&mvm->mutex);
 
+       /* allow one more restart that we're provoking here */
+       if (mvm->restart_fw >= 0)
+               mvm->restart_fw++;
+
        /* take the return value to make compiler happy - it will fail anyway */
        ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
 
        mutex_unlock(&mvm->mutex);
 
-       iwlwifi_mod_params.restart_fw = restart_fw;
-
        return count;
 }
 
@@ -661,8 +775,14 @@ static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
        case MVM_DEBUGFS_BF_ROAMING_STATE:
                dbgfs_bf->bf_roaming_state = value;
                break;
-       case MVM_DEBUGFS_BF_TEMPERATURE_DELTA:
-               dbgfs_bf->bf_temperature_delta = value;
+       case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
+               dbgfs_bf->bf_temp_threshold = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
+               dbgfs_bf->bf_temp_fast_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
+               dbgfs_bf->bf_temp_slow_filter = value;
                break;
        case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
                dbgfs_bf->bf_enable_beacon_filter = value;
@@ -721,13 +841,27 @@ static ssize_t iwl_dbgfs_bf_params_write(struct file *file,
                    value > IWL_BF_ROAMING_STATE_MAX)
                        return -EINVAL;
                param = MVM_DEBUGFS_BF_ROAMING_STATE;
-       } else if (!strncmp("bf_temperature_delta=", buf, 21)) {
-               if (sscanf(buf+21, "%d", &value) != 1)
+       } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
+               if (sscanf(buf+18, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
+                   value > IWL_BF_TEMP_THRESHOLD_MAX)
                        return -EINVAL;
-               if (value < IWL_BF_TEMPERATURE_DELTA_MIN ||
-                   value > IWL_BF_TEMPERATURE_DELTA_MAX)
+               param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+       } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
                        return -EINVAL;
-               param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA;
+               if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
+                   value > IWL_BF_TEMP_FAST_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
+       } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
+                   value > IWL_BF_TEMP_SLOW_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
        } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
                if (sscanf(buf+24, "%d", &value) != 1)
                        return -EINVAL;
@@ -769,10 +903,7 @@ static ssize_t iwl_dbgfs_bf_params_write(struct file *file,
        if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) {
                ret = iwl_mvm_disable_beacon_filter(mvm, vif);
        } else {
-               if (mvmvif->bf_enabled)
-                       ret = iwl_mvm_enable_beacon_filter(mvm, vif);
-               else
-                       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif);
        }
        mutex_unlock(&mvm->mutex);
 
@@ -789,41 +920,41 @@ static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
        int pos = 0;
        const size_t bufsz = sizeof(buf);
        struct iwl_beacon_filter_cmd cmd = {
-               .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,
-               .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT,
-               .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,
-               .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,
-               .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT,
-               .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,
-               .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),
-               .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT),
-               .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT,
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter =
+                       cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
+               .ba_enable_beacon_abort =
+                       cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
        };
 
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       if (mvmvif->bf_enabled)
-               cmd.bf_enable_beacon_filter = 1;
+       if (mvmvif->bf_data.bf_enabled)
+               cmd.bf_enable_beacon_filter = cpu_to_le32(1);
        else
                cmd.bf_enable_beacon_filter = 0;
 
        pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
-                        cmd.bf_energy_delta);
+                        le32_to_cpu(cmd.bf_energy_delta));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
-                        cmd.bf_roaming_energy_delta);
+                        le32_to_cpu(cmd.bf_roaming_energy_delta));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
-                        cmd.bf_roaming_state);
-       pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n",
-                        cmd.bf_temperature_delta);
+                        le32_to_cpu(cmd.bf_roaming_state));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
+                        le32_to_cpu(cmd.bf_temp_threshold));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_fast_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_slow_filter));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
-                        cmd.bf_enable_beacon_filter);
+                        le32_to_cpu(cmd.bf_enable_beacon_filter));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
-                        cmd.bf_debug_flag);
+                        le32_to_cpu(cmd.bf_debug_flag));
        pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
-                        cmd.bf_escape_timer);
+                        le32_to_cpu(cmd.bf_escape_timer));
        pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
-                        cmd.ba_escape_timer);
+                        le32_to_cpu(cmd.ba_escape_timer));
        pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
-                        cmd.ba_enable_beacon_abort);
+                        le32_to_cpu(cmd.ba_enable_beacon_abort));
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -934,6 +1065,7 @@ MVM_DEBUGFS_READ_FILE_OPS(stations);
 MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
 MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
@@ -957,6 +1089,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
 #ifdef CONFIG_PM_SLEEP
        MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
index 6f8b2c1..df72fcd 100644 (file)
@@ -98,34 +98,63 @@ enum iwl_proto_offloads {
        IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
 };
 
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS       2
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1    2
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2    6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   6
 
 /**
- * struct iwl_proto_offload_cmd - ARP/NS offload configuration
+ * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
  * @enabled: enable flags
  * @remote_ipv4_addr: remote address to answer to (or zero if all)
  * @host_ipv4_addr: our IPv4 address to respond to queries for
  * @arp_mac_addr: our MAC address for ARP responses
- * @remote_ipv6_addr: remote address to answer to (or zero if all)
- * @solicited_node_ipv6_addr: broken -- solicited node address exists
- *     for each target address
- * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @reserved: unused
  */
-struct iwl_proto_offload_cmd {
+struct iwl_proto_offload_cmd_common {
        __le32 enabled;
        __be32 remote_ipv4_addr;
        __be32 host_ipv4_addr;
        u8 arp_mac_addr[ETH_ALEN];
-       __le16 reserved1;
+       __le16 reserved;
+} __packed;
 
+/**
+ * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v1 {
+       struct iwl_proto_offload_cmd_common common;
        u8 remote_ipv6_addr[16];
        u8 solicited_node_ipv6_addr[16];
-       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16];
        u8 ndp_mac_addr[ETH_ALEN];
        __le16 reserved2;
 } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
 
+/**
+ * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v2 {
+       struct iwl_proto_offload_cmd_common common;
+       u8 remote_ipv6_addr[16];
+       u8 solicited_node_ipv6_addr[16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16];
+       u8 ndp_mac_addr[ETH_ALEN];
+       u8 numValidIPv6Addresses;
+       u8 reserved2[3];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
+
 
 /*
  * WOWLAN_PATTERNS
index a6da359..8e7ab41 100644 (file)
  *             '1' Driver enables PM (use rest of parameters)
  * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
  *             '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ *             access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ *             PBW Snoozing enabled
  * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
  * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
 */
@@ -86,6 +90,8 @@ enum iwl_power_flags {
        POWER_FLAGS_POWER_SAVE_ENA_MSK          = BIT(0),
        POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK    = BIT(1),
        POWER_FLAGS_SKIP_OVER_DTIM_MSK          = BIT(2),
+       POWER_FLAGS_SNOOZE_ENA_MSK              = BIT(5),
+       POWER_FLAGS_BT_SCO_ENA                  = BIT(8),
        POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(9),
        POWER_FLAGS_LPRX_ENA_MSK                = BIT(11),
 };
@@ -93,7 +99,8 @@ enum iwl_power_flags {
 #define IWL_POWER_VEC_SIZE 5
 
 /**
- * struct iwl_powertable_cmd - Power Table Command
+ * struct iwl_powertable_cmd - legacy power command. Beside old API support this
+ *     is used also with a new power API for device wide power settings.
  * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
  *
  * @flags:             Power table command flags from POWER_FLAGS_*
@@ -125,6 +132,76 @@ struct iwl_powertable_cmd {
 } __packed;
 
 /**
+ * struct iwl_mac_power_cmd - New power command containing uAPSD support
+ * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
+ * @id_and_color:      MAC contex identifier
+ * @flags:             Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds:        Keep alive period in seconds. Default - 25 sec.
+ *                     Minimum allowed:- 3 * DTIM. Keep alive period must be
+ *                     set regardless of power scheme or current power state.
+ *                     FW use this value also when PM is disabled.
+ * @rx_data_timeout:    Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - legacy PM
+ * @tx_data_timeout:    Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - legacy PM
+ * @sleep_interval:    not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ *                     is set. For example, if it is required to skip over
+ *                     one DTIM, this value need to be set to 2 (DTIM periods).
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ *                     Default: 80dbm
+ * @num_skip_dtim:     Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval:   Maximum time between attempts to retrieve buffered data
+ *                     from the AP [msec]
+ * @snooze_window:     A window of time in which PBW snoozing insures that all
+ *                     packets received. It is also the minimum time from last
+ *                     received unicast RX packet, before client stops snoozing
+ *                     for data. [msec]
+ * @snooze_step:       TBD
+ * @qndp_tid:          TID client shall use for uAPSD QNDP triggers
+ * @uapsd_ac_flags:    Set trigger-enabled and delivery-enabled indication for
+ *                     each corresponding AC.
+ *                     Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
+ * @uapsd_max_sp:      Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
+ *                     values.
+ * @heavy_tx_thld_packets:     TX threshold measured in number of packets
+ * @heavy_rx_thld_packets:     RX threshold measured in number of packets
+ * @heavy_tx_thld_percentage:  TX threshold measured in load's percentage
+ * @heavy_rx_thld_percentage:  RX threshold measured in load's percentage
+ * @limited_ps_threshold:
+*/
+struct iwl_mac_power_cmd {
+       /* CONTEXT_DESC_API_T_VER_1 */
+       __le32 id_and_color;
+
+       /* CLIENT_PM_POWER_TABLE_S_VER_1 */
+       __le16 flags;
+       __le16 keep_alive_seconds;
+       __le32 rx_data_timeout;
+       __le32 tx_data_timeout;
+       __le32 rx_data_timeout_uapsd;
+       __le32 tx_data_timeout_uapsd;
+       u8 lprx_rssi_threshold;
+       u8 skip_dtim_periods;
+       __le16 snooze_interval;
+       __le16 snooze_window;
+       u8 snooze_step;
+       u8 qndp_tid;
+       u8 uapsd_ac_flags;
+       u8 uapsd_max_sp;
+       u8 heavy_tx_thld_packets;
+       u8 heavy_rx_thld_packets;
+       u8 heavy_tx_thld_percentage;
+       u8 heavy_rx_thld_percentage;
+       u8 limited_ps_threshold;
+       u8 reserved;
+} __packed;
+
+/**
  * struct iwl_beacon_filter_cmd
  * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
  * @id_and_color: MAC contex identifier
@@ -143,11 +220,21 @@ struct iwl_powertable_cmd {
  *      calculated for current beacon is less than the threshold, use
  *      Roaming Energy Delta Threshold, otherwise use normal Energy Delta
  *      Threshold. Typical energy threshold is -72dBm.
- * @bf_temperature_delta: Send Beacon to driver if delta in temperature values
- *      calculated for this and the last passed beacon is greater than  this
- *      threshold. Zero value means that the temperature changeis ignored for
+ * @bf_temp_threshold: This threshold determines the type of temperature
+ *     filtering (Slow or Fast) that is selected (Units are in Celsuis):
+ *      If the current temperature is above this threshold - Fast filter
+ *     will be used, If the current temperature is below this threshold -
+ *     Slow filter will be used.
+ * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values
+ *      calculated for this and the last passed beacon is greater than this
+ *      threshold. Zero value means that the temperature change is ignored for
  *      beacon filtering; beacons will not be  forced to be sent to driver
  *      regardless of whether its temerature has been changed.
+ * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
+ *      calculated for this and the last passed beacon is greater than this
+ *      threshold. Zero value means that the temperature change is ignored for
+ *      beacon filtering; beacons will not be forced to be sent to driver
+ *      regardless of whether its temerature has been changed.
  * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
  * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
  *      for a specific period of time. Units: Beacons.
@@ -156,17 +243,17 @@ struct iwl_powertable_cmd {
  * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
  */
 struct iwl_beacon_filter_cmd {
-       u8 bf_energy_delta;
-       u8 bf_roaming_energy_delta;
-       u8 bf_roaming_state;
-       u8 bf_temperature_delta;
-       u8 bf_enable_beacon_filter;
-       u8 bf_debug_flag;
-       __le16 reserved1;
+       __le32 bf_energy_delta;
+       __le32 bf_roaming_energy_delta;
+       __le32 bf_roaming_state;
+       __le32 bf_temp_threshold;
+       __le32 bf_temp_fast_filter;
+       __le32 bf_temp_slow_filter;
+       __le32 bf_enable_beacon_filter;
+       __le32 bf_debug_flag;
        __le32 bf_escape_timer;
        __le32 ba_escape_timer;
-       u8 ba_enable_beacon_abort;
-       u8 reserved2[3];
+       __le32 ba_enable_beacon_abort;
 } __packed;
 
 /* Beacon filtering and beacon abort */
@@ -182,9 +269,17 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ROAMING_STATE_MAX 255
 #define IWL_BF_ROAMING_STATE_MIN 0
 
-#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5
-#define IWL_BF_TEMPERATURE_DELTA_MAX 255
-#define IWL_BF_TEMPERATURE_DELTA_MIN 0
+#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112
+#define IWL_BF_TEMP_THRESHOLD_MAX 255
+#define IWL_BF_TEMP_THRESHOLD_MIN 0
+
+#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1
+#define IWL_BF_TEMP_FAST_FILTER_MAX 255
+#define IWL_BF_TEMP_FAST_FILTER_MIN 0
+
+#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5
+#define IWL_BF_TEMP_SLOW_FILTER_MAX 255
+#define IWL_BF_TEMP_SLOW_FILTER_MIN 0
 
 #define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1
 
@@ -194,19 +289,23 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_ESCAPE_TIMER_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
-#define IWL_BA_ESCAPE_TIMER_DEFAULT 3
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D3 6
 #define IWL_BA_ESCAPE_TIMER_MAX 1024
 #define IWL_BA_ESCAPE_TIMER_MIN 0
 
 #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1
 
-#define IWL_BF_CMD_CONFIG_DEFAULTS                                     \
-       .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,                 \
-       .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \
-       .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,               \
-       .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,       \
-       .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT,                     \
-       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),    \
+#define IWL_BF_CMD_CONFIG_DEFAULTS                                          \
+       .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT),         \
+       .bf_roaming_energy_delta =                                           \
+               cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT),            \
+       .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT),       \
+       .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT),     \
+       .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \
+       .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \
+       .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT),             \
+       .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT),         \
        .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT)
 
 #endif
index 365095a..83cb9b9 100644 (file)
@@ -137,6 +137,8 @@ struct iwl_ssid_ie {
  *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
  *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
  *@SCAN_FLAGS_FRAGMENTED_SCAN:
+ *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
+ *     in the past hour, even if they are marked as passive.
  */
 enum iwl_scan_flags {
        SCAN_FLAGS_PERIODIC_SCAN                = BIT(0),
@@ -144,6 +146,7 @@ enum iwl_scan_flags {
        SCAN_FLAGS_DELAYED_SCAN_LOWBAND         = BIT(2),
        SCAN_FLAGS_DELAYED_SCAN_HIGHBAND        = BIT(3),
        SCAN_FLAGS_FRAGMENTED_SCAN              = BIT(4),
+       SCAN_FLAGS_PASSIVE2ACTIVE               = BIT(5),
 };
 
 /**
@@ -178,7 +181,7 @@ enum iwl_scan_type {
  * @quiet_time: in msecs, dwell this time for active scan on quiet channels
  * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
  *     this number of packets were received (typically 1)
- * @passive2active: is auto switching from passive to active allowed (0 or 1)
+ * @passive2active: is auto switching from passive to active during scan allowed
  * @rxchain_sel_flags: RXON_RX_CHAIN_*
  * @max_out_time: in usecs, max out of serving channel time
  * @suspend_time: how long to pause scan when returning to service channel:
index 700cce7..d606197 100644 (file)
@@ -91,7 +91,6 @@
  * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
  * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
  * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
- * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
  * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
  * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
  * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
@@ -120,7 +119,6 @@ enum iwl_tx_flags {
        TX_CMD_FLG_RESP_TO_DRV          = BIT(21),
        TX_CMD_FLG_CCMP_AGG             = BIT(22),
        TX_CMD_FLG_TKIP_MIC_DONE        = BIT(23),
-       TX_CMD_FLG_CTS_ONLY             = BIT(24),
        TX_CMD_FLG_DUR                  = BIT(25),
        TX_CMD_FLG_FW_DROP              = BIT(26),
        TX_CMD_FLG_EXEC_PAPD            = BIT(27),
index cbfb3be..66264cc 100644 (file)
@@ -136,7 +136,7 @@ enum {
        CALIB_RES_NOTIF_PHY_DB = 0x6b,
        /* PHY_DB_CMD = 0x6c, */
 
-       /* Power */
+       /* Power - legacy power table command */
        POWER_TABLE_CMD = 0x77,
 
        /* Thermal Throttling*/
@@ -159,6 +159,7 @@ enum {
        TX_ANT_CONFIGURATION_CMD = 0x98,
        BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
+       REDUCE_TX_POWER_CMD = 0x9f,
 
        /* RF-KILL commands and notifications */
        CARD_STATE_CMD = 0xa0,
@@ -166,6 +167,9 @@ enum {
 
        MISSED_BEACONS_NOTIFICATION = 0xa2,
 
+       /* Power - new power table command */
+       MAC_PM_POWER_TABLE = 0xa9,
+
        REPLY_RX_PHY_CMD = 0xc0,
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
@@ -223,6 +227,19 @@ struct iwl_tx_ant_cfg_cmd {
        __le32 valid;
 } __packed;
 
+/**
+ * struct iwl_reduce_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @flags: (reserved for future implementation)
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in dBms.
+ */
+struct iwl_reduce_tx_power_cmd {
+       u8 flags;
+       u8 mac_context_id;
+       __le16 pwr_restriction;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
+
 /*
  * Calibration control struct.
  * Sent as part of the phy configuration command.
@@ -482,71 +499,199 @@ enum iwl_time_event_type {
        TE_MAX
 }; /* MAC_EVENT_TYPE_API_E_VER_1 */
 
+
+
+/* Time event - defines for command API v1 */
+
+/*
+ * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ *     the first fragment is scheduled.
+ * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ *     the first 2 fragments are scheduled.
+ * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ *     number of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+       TE_V1_FRAG_NONE = 0,
+       TE_V1_FRAG_SINGLE = 1,
+       TE_V1_FRAG_DUAL = 2,
+       TE_V1_FRAG_ENDLESS = 0xffffffff
+};
+
+/* If a Time Event can be fragmented, this is the max number of fragments */
+#define TE_V1_FRAG_MAX_MSK     0x0fffffff
+/* Repeat the time event endlessly (until removed) */
+#define TE_V1_REPEAT_ENDLESS   0xffffffff
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_V1_REPEAT_MAX_MSK_V1        0x0fffffff
+
 /* Time Event dependencies: none, on another TE, or in a specific time */
 enum {
-       TE_INDEPENDENT          = 0,
-       TE_DEP_OTHER            = 1,
-       TE_DEP_TSF              = 2,
-       TE_EVENT_SOCIOPATHIC    = 4,
+       TE_V1_INDEPENDENT               = 0,
+       TE_V1_DEP_OTHER                 = BIT(0),
+       TE_V1_DEP_TSF                   = BIT(1),
+       TE_V1_EVENT_SOCIOPATHIC         = BIT(2),
 }; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
+
 /*
+ * @TE_V1_NOTIF_NONE: no notifications
+ * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ *
  * Supported Time event notifications configuration.
  * A notification (both event and fragment) includes a status indicating weather
  * the FW was able to schedule the event or not. For fragment start/end
  * notification the status is always success. There is no start/end fragment
  * notification for monolithic events.
- *
- * @TE_NOTIF_NONE: no notifications
- * @TE_NOTIF_HOST_EVENT_START: request/receive notification on event start
- * @TE_NOTIF_HOST_EVENT_END:request/receive notification on event end
- * @TE_NOTIF_INTERNAL_EVENT_START: internal FW use
- * @TE_NOTIF_INTERNAL_EVENT_END: internal FW use.
- * @TE_NOTIF_HOST_FRAG_START: request/receive notification on frag start
- * @TE_NOTIF_HOST_FRAG_END:request/receive notification on frag end
- * @TE_NOTIF_INTERNAL_FRAG_START: internal FW use.
- * @TE_NOTIF_INTERNAL_FRAG_END: internal FW use.
  */
 enum {
-       TE_NOTIF_NONE = 0,
-       TE_NOTIF_HOST_EVENT_START = 0x1,
-       TE_NOTIF_HOST_EVENT_END = 0x2,
-       TE_NOTIF_INTERNAL_EVENT_START = 0x4,
-       TE_NOTIF_INTERNAL_EVENT_END = 0x8,
-       TE_NOTIF_HOST_FRAG_START = 0x10,
-       TE_NOTIF_HOST_FRAG_END = 0x20,
-       TE_NOTIF_INTERNAL_FRAG_START = 0x40,
-       TE_NOTIF_INTERNAL_FRAG_END = 0x80
+       TE_V1_NOTIF_NONE = 0,
+       TE_V1_NOTIF_HOST_EVENT_START = BIT(0),
+       TE_V1_NOTIF_HOST_EVENT_END = BIT(1),
+       TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2),
+       TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3),
+       TE_V1_NOTIF_HOST_FRAG_START = BIT(4),
+       TE_V1_NOTIF_HOST_FRAG_END = BIT(5),
+       TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6),
+       TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7),
 }; /* MAC_EVENT_ACTION_API_E_VER_2 */
 
+
+/**
+ * struct iwl_time_event_cmd_api_v1 - configuring Time Events
+ * with struct MAC_TIME_EVENT_DATA_API_S_VER_1 (see also
+ * with version 2. determined by IWL_UCODE_TLV_FLAGS)
+ * ( TIME_EVENT_CMD = 0x29 )
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @id: this field has two meanings, depending on the action:
+ *     If the action is ADD, then it means the type of event to add.
+ *     For all other actions it is the unique event ID assigned when the
+ *     event was added by the FW.
+ * @apply_time: When to start the Time Event (in GP2)
+ * @max_delay: maximum delay to event's start (apply time), in TU
+ * @depends_on: the unique ID of the event we depend on (if any)
+ * @interval: interval between repetitions, in TU
+ * @interval_reciprocal: 2^32 / interval
+ * @duration: duration of event in TU
+ * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
+ * @dep_policy: one of TE_V1_INDEPENDENT, TE_V1_DEP_OTHER, TE_V1_DEP_TSF
+ *     and TE_V1_EVENT_SOCIOPATHIC
+ * @is_present: 0 or 1, are we present or absent during the Time Event
+ * @max_frags: maximal number of fragments the Time Event can be divided to
+ * @notify: notifications using TE_V1_NOTIF_* (whom to notify when)
+ */
+struct iwl_time_event_cmd_v1 {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       __le32 id;
+       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
+       __le32 apply_time;
+       __le32 max_delay;
+       __le32 dep_policy;
+       __le32 depends_on;
+       __le32 is_present;
+       __le32 max_frags;
+       __le32 interval;
+       __le32 interval_reciprocal;
+       __le32 duration;
+       __le32 repeat;
+       __le32 notify;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
+
+
+/* Time event - defines for command API v2 */
+
 /*
- * @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
- * @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only
  *  the first fragment is scheduled.
- * @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only
  *  the first 2 fragments are scheduled.
- * @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number
- *  of fragments are valid.
+ * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ *  number of fragments are valid.
  *
  * Other than the constant defined above, specifying a fragmentation value 'x'
  * means that the event can be fragmented but only the first 'x' will be
  * scheduled.
  */
 enum {
-       TE_FRAG_NONE = 0,
-       TE_FRAG_SINGLE = 1,
-       TE_FRAG_DUAL = 2,
-       TE_FRAG_ENDLESS = 0xffffffff
+       TE_V2_FRAG_NONE = 0,
+       TE_V2_FRAG_SINGLE = 1,
+       TE_V2_FRAG_DUAL = 2,
+       TE_V2_FRAG_MAX = 0xfe,
+       TE_V2_FRAG_ENDLESS = 0xff
 };
 
 /* Repeat the time event endlessly (until removed) */
-#define TE_REPEAT_ENDLESS      (0xffffffff)
+#define TE_V2_REPEAT_ENDLESS   0xff
 /* If a Time Event has bounded repetitions, this is the maximal value */
-#define TE_REPEAT_MAX_MSK      (0x0fffffff)
-/* If a Time Event can be fragmented, this is the max number of fragments */
-#define TE_FRAG_MAX_MSK                (0x0fffffff)
+#define TE_V2_REPEAT_MAX       0xfe
+
+#define TE_V2_PLACEMENT_POS    12
+#define TE_V2_ABSENCE_POS      15
+
+/* Time event policy values (for time event cmd api v2)
+ * A notification (both event and fragment) includes a status indicating weather
+ * the FW was able to schedule the event or not. For fragment start/end
+ * notification the status is always success. There is no start/end fragment
+ * notification for monolithic events.
+ *
+ * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable
+ * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ * @TE_V2_DEP_OTHER: depends on another time event
+ * @TE_V2_DEP_TSF: depends on a specific time
+ * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC
+ * @TE_V2_ABSENCE: are we present or absent during the Time Event.
+ */
+enum {
+       TE_V2_DEFAULT_POLICY = 0x0,
+
+       /* notifications (event start/stop, fragment start/stop) */
+       TE_V2_NOTIF_HOST_EVENT_START = BIT(0),
+       TE_V2_NOTIF_HOST_EVENT_END = BIT(1),
+       TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2),
+       TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3),
+
+       TE_V2_NOTIF_HOST_FRAG_START = BIT(4),
+       TE_V2_NOTIF_HOST_FRAG_END = BIT(5),
+       TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6),
+       TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7),
+
+       TE_V2_NOTIF_MSK = 0xff,
+
+       /* placement characteristics */
+       TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS),
+       TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1),
+       TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2),
+
+       /* are we present or absent during the Time Event. */
+       TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS),
+};
 
 /**
- * struct iwl_time_event_cmd - configuring Time Events
+ * struct iwl_time_event_cmd_api_v2 - configuring Time Events
+ * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also
+ * with version 1. determined by IWL_UCODE_TLV_FLAGS)
  * ( TIME_EVENT_CMD = 0x29 )
  * @id_and_color: ID and color of the relevant MAC
  * @action: action to perform, one of FW_CTXT_ACTION_*
@@ -558,32 +703,30 @@ enum {
  * @max_delay: maximum delay to event's start (apply time), in TU
  * @depends_on: the unique ID of the event we depend on (if any)
  * @interval: interval between repetitions, in TU
- * @interval_reciprocal: 2^32 / interval
  * @duration: duration of event in TU
  * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
- * @dep_policy: one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
- * @is_present: 0 or 1, are we present or absent during the Time Event
  * @max_frags: maximal number of fragments the Time Event can be divided to
- * @notify: notifications using TE_NOTIF_* (whom to notify when)
+ * @policy: defines whether uCode shall notify the host or other uCode modules
+ *     on event and/or fragment start and/or end
+ *     using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
+ *     TE_EVENT_SOCIOPATHIC
+ *     using TE_ABSENCE and using TE_NOTIF_*
  */
-struct iwl_time_event_cmd {
+struct iwl_time_event_cmd_v2 {
        /* COMMON_INDEX_HDR_API_S_VER_1 */
        __le32 id_and_color;
        __le32 action;
        __le32 id;
-       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
+       /* MAC_TIME_EVENT_DATA_API_S_VER_2 */
        __le32 apply_time;
        __le32 max_delay;
-       __le32 dep_policy;
        __le32 depends_on;
-       __le32 is_present;
-       __le32 max_frags;
        __le32 interval;
-       __le32 interval_reciprocal;
        __le32 duration;
-       __le32 repeat;
-       __le32 notify;
-} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
+       u8 repeat;
+       u8 max_frags;
+       __le16 policy;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */
 
 /**
  * struct iwl_time_event_resp - response structure to iwl_time_event_cmd
@@ -765,6 +908,14 @@ struct iwl_phy_context_cmd {
 } __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
 
 #define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
+#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
+#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
+#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
+#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
+#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
+#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+
 #define IWL_RX_INFO_AGC_IDX 1
 #define IWL_RX_INFO_RSSI_AB_IDX 2
 #define IWL_OFDM_AGC_A_MSK 0x0000007f
@@ -1170,7 +1321,7 @@ struct mvm_statistics_general {
        struct mvm_statistics_general_common common;
        __le32 beacon_filtered;
        __le32 missed_beacons;
-       __s8 beacon_filter_everage_energy;
+       __s8 beacon_filter_average_energy;
        __s8 beacon_filter_reason;
        __s8 beacon_filter_current_energy;
        __s8 beacon_filter_reserved;
index cd7c003..c76299a 100644 (file)
 
 #define UCODE_VALID_OK cpu_to_le32(0x1)
 
-/* Default calibration values for WkP - set to INIT image w/o running */
-static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
-static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
-
-struct iwl_calib_default_data {
-       u16 size;
-       void *data;
-};
-
-#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
-
-static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
-       [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
-       [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
-};
-
 struct iwl_mvm_alive_data {
        bool valid;
        u32 scd_base_addr;
@@ -248,40 +232,6 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
                                    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
 }
 
-static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
-{
-       u8 cmd_raw[16]; /* holds the variable size commands */
-       struct iwl_set_calib_default_cmd *cmd =
-               (struct iwl_set_calib_default_cmd *)cmd_raw;
-       int ret, i;
-
-       /* Setting default values for calibrations we don't run */
-       for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
-               u16 cmd_len;
-
-               if (wkp_calib_default_data[i].size == 0)
-                       continue;
-
-               memset(cmd_raw, 0, sizeof(cmd_raw));
-               cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
-               cmd->calib_index = cpu_to_le16(i);
-               cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
-               if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
-                             "Need to enlarge cmd_raw to %d\n", cmd_len))
-                       break;
-               memcpy(cmd->data, wkp_calib_default_data[i].data,
-                      wkp_calib_default_data[i].size);
-               ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
-                                          sizeof(*cmd) +
-                                          wkp_calib_default_data[i].size,
-                                          cmd);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
 int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 {
        struct iwl_notification_wait calib_wait;
@@ -342,11 +292,6 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        if (ret)
                goto error;
 
-       /* need to set default values */
-       ret = iwl_set_default_calibrations(mvm);
-       if (ret)
-               goto error;
-
        /*
         * Send phy configurations command to init uCode
         * to start the 16.0 uCode init image internal calibrations.
index 94aae9c..5fe23a5 100644 (file)
@@ -264,7 +264,8 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                return 0;
 
        /* Therefore, in recovery, we can't get here */
-       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+       if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
+               return -EBUSY;
 
        mvmvif->id = find_first_bit(data.available_mac_ids,
                                    NUM_MAC_INDEX_DRIVER);
index f19baf0..9833cdf 100644 (file)
@@ -153,7 +153,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
-                   IEEE80211_HW_CONNECTION_MONITOR;
+                   IEEE80211_HW_CONNECTION_MONITOR |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+                   IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                   IEEE80211_HW_SUPPORTS_UAPSD;
 
        hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -188,6 +191,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+       hw->uapsd_queues = IWL_UAPSD_AC_INFO;
+       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -506,7 +511,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
-       /* Allocate resources for the MAC context, and add it the the fw  */
+       /* Allocate resources for the MAC context, and add it to the fw  */
        ret = iwl_mvm_mac_ctxt_init(mvm, vif);
        if (ret)
                goto out_unlock;
@@ -552,6 +557,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                        goto out_release;
                }
 
+               iwl_mvm_vif_dbgfs_register(mvm, vif);
                goto out_unlock;
        }
 
@@ -566,16 +572,18 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        iwl_mvm_power_update_mode(mvm, vif);
 
        /* beacon filtering */
+       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       if (ret)
+               goto out_remove_mac;
+
        if (!mvm->bf_allowed_vif &&
-           vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
                mvm->bf_allowed_vif = mvmvif;
-               vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+               vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                                    IEEE80211_VIF_SUPPORTS_CQM_RSSI;
        }
 
-       ret = iwl_mvm_disable_beacon_filter(mvm, vif);
-       if (ret)
-               goto out_release;
-
        /*
         * P2P_DEVICE interface does not have a channel context assigned to it,
         * so a dedicated PHY context is allocated to it and the corresponding
@@ -586,7 +594,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
                if (!mvmvif->phy_ctxt) {
                        ret = -ENOSPC;
-                       goto out_remove_mac;
+                       goto out_free_bf;
                }
 
                iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
@@ -610,6 +618,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        iwl_mvm_binding_remove_vif(mvm, vif);
  out_unref_phy:
        iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+ out_free_bf:
+       if (mvm->bf_allowed_vif == mvmvif) {
+               mvm->bf_allowed_vif = NULL;
+               vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+                                      IEEE80211_VIF_SUPPORTS_CQM_RSSI);
+       }
  out_remove_mac:
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_mac_ctxt_remove(mvm, vif);
@@ -674,7 +688,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
 
        if (mvm->bf_allowed_vif == mvmvif) {
                mvm->bf_allowed_vif = NULL;
-               vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
+               vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+                                      IEEE80211_VIF_SUPPORTS_CQM_RSSI);
        }
 
        iwl_mvm_vif_dbgfs_clean(mvm, vif);
@@ -719,6 +734,20 @@ out_release:
        mutex_unlock(&mvm->mutex);
 }
 
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               s8 tx_power)
+{
+       /* FW is in charge of regulatory enforcement */
+       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
+               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
+               .pwr_restriction = cpu_to_le16(tx_power),
+       };
+
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+                                   sizeof(reduce_txpwr_cmd),
+                                   &reduce_txpwr_cmd);
+}
+
 static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
 {
        return 0;
@@ -766,7 +795,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
-                       iwl_mvm_bt_coex_vif_assoc(mvm, vif);
                        iwl_mvm_configure_mcast_filter(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
@@ -779,9 +807,19 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
-               ret = iwl_mvm_power_update_mode(mvm, vif);
-               if (ret)
-                       IWL_ERR(mvm, "failed to update power mode\n");
+
+               /* reset rssi values */
+               mvmvif->bf_data.ave_beacon_signal = 0;
+
+               if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
+                       /* Workaround for FW bug, otherwise FW disables device
+                        * power save upon disassociation
+                        */
+                       ret = iwl_mvm_power_update_mode(mvm, vif);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update power mode\n");
+               }
+               iwl_mvm_bt_coex_vif_assoc(mvm, vif);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
@@ -789,11 +827,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                 */
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
-       } else if (changes & BSS_CHANGED_PS) {
+       } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) {
                ret = iwl_mvm_power_update_mode(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
+       if (changes & BSS_CHANGED_TXPOWER) {
+               IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+                               bss_conf->txpower);
+               iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+       }
+
+       if (changes & BSS_CHANGED_CQM) {
+               IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
+               /* reset cqm events tracking */
+               mvmvif->bf_data.last_cqm_event = 0;
+               ret = iwl_mvm_update_beacon_filter(mvm, vif);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update CQM thresholds\n");
+       }
 }
 
 static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
index 420e82d..b038927 100644 (file)
@@ -76,6 +76,7 @@
 #include "iwl-trans.h"
 #include "sta.h"
 #include "fw-api.h"
+#include "constants.h"
 
 #define IWL_INVALID_MAC80211_QUEUE     0xff
 #define IWL_MVM_MAX_ADDRESSES          5
@@ -91,6 +92,9 @@ enum iwl_mvm_tx_fifo {
 };
 
 extern struct ieee80211_ops iwl_mvm_hw_ops;
+extern const struct iwl_mvm_power_ops pm_legacy_ops;
+extern const struct iwl_mvm_power_ops pm_mac_ops;
+
 /**
  * struct iwl_mvm_mod_params - module parameters for iwlmvm
  * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
@@ -149,6 +153,22 @@ enum iwl_power_scheme {
 };
 
 #define IWL_CONN_MAX_LISTEN_INTERVAL   70
+#define IWL_UAPSD_AC_INFO              (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\
+                                        IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\
+                                        IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\
+                                        IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+#define IWL_UAPSD_MAX_SP               IEEE80211_WMM_IE_STA_QOSINFO_SP_2
+
+struct iwl_mvm_power_ops {
+       int (*power_update_mode)(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
+       int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               char *buf, int bufsz);
+#endif
+};
+
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 enum iwl_dbgfs_pm_mask {
@@ -160,10 +180,11 @@ enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5),
        MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
        MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
+       MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
 };
 
 struct iwl_dbgfs_pm {
-       u8 keep_alive_seconds;
+       u16 keep_alive_seconds;
        u32 rx_data_timeout;
        u32 tx_data_timeout;
        bool skip_over_dtim;
@@ -171,6 +192,7 @@ struct iwl_dbgfs_pm {
        bool disable_power_off;
        bool lprx_ena;
        u32 lprx_rssi_threshold;
+       bool snooze_ena;
        int mask;
 };
 
@@ -180,24 +202,28 @@ enum iwl_dbgfs_bf_mask {
        MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
        MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
        MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
-       MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3),
-       MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4),
-       MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5),
-       MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6),
-       MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7),
-       MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8),
+       MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3),
+       MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4),
+       MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5),
+       MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6),
+       MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7),
+       MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8),
+       MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9),
+       MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10),
 };
 
 struct iwl_dbgfs_bf {
-       u8 bf_energy_delta;
-       u8 bf_roaming_energy_delta;
-       u8 bf_roaming_state;
-       u8 bf_temperature_delta;
-       u8 bf_enable_beacon_filter;
-       u8 bf_debug_flag;
+       u32 bf_energy_delta;
+       u32 bf_roaming_energy_delta;
+       u32 bf_roaming_state;
+       u32 bf_temp_threshold;
+       u32 bf_temp_fast_filter;
+       u32 bf_temp_slow_filter;
+       u32 bf_enable_beacon_filter;
+       u32 bf_debug_flag;
        u32 bf_escape_timer;
        u32 ba_escape_timer;
-       u8 ba_enable_beacon_abort;
+       u32 ba_enable_beacon_abort;
        int mask;
 };
 #endif
@@ -209,6 +235,21 @@ enum iwl_mvm_smps_type_request {
 };
 
 /**
+* struct iwl_mvm_vif_bf_data - beacon filtering related data
+* @bf_enabled: indicates if beacon filtering is enabled
+* @ba_enabled: indicated if beacon abort is enabled
+* @last_beacon_signal: last beacon rssi signal in dbm
+* @ave_beacon_signal: average beacon signal
+* @last_cqm_event: rssi of the last cqm event
+*/
+struct iwl_mvm_vif_bf_data {
+       bool bf_enabled;
+       bool ba_enabled;
+       s8 ave_beacon_signal;
+       s8 last_cqm_event;
+};
+
+/**
  * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
  * @id: between 0 and 3
  * @color: to solve races upon MAC addition and removal
@@ -233,8 +274,7 @@ struct iwl_mvm_vif {
        bool uploaded;
        bool ap_active;
        bool monitor_active;
-       /* indicate whether beacon filtering is enabled */
-       bool bf_enabled;
+       struct iwl_mvm_vif_bf_data bf_data;
 
        u32 ap_beacon_time;
 
@@ -268,7 +308,7 @@ struct iwl_mvm_vif {
 
 #if IS_ENABLED(CONFIG_IPV6)
        /* IPv6 addresses for WoWLAN */
-       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
+       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
        int num_target_ipv6_addrs;
 #endif
 #endif
@@ -402,6 +442,8 @@ struct iwl_mvm {
 
        struct iwl_notif_wait_data notif_wait;
 
+       struct mvm_statistics_rx rx_stats;
+
        unsigned long transport_queue_stop;
        u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
        atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
@@ -459,6 +501,9 @@ struct iwl_mvm {
         */
        u8 vif_count;
 
+       /* -1 for always, 0 for never, >0 for that many times */
+       s8 restart_fw;
+
        struct led_classdev led;
 
        struct ieee80211_vif *p2p_device_vif;
@@ -482,6 +527,8 @@ struct iwl_mvm {
        /* Thermal Throttling and CTkill */
        struct iwl_mvm_tt_mgmt thermal_throttle;
        s32 temperature;        /* Celsius */
+
+       const struct iwl_mvm_power_ops *pm_ops;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -525,6 +572,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
                                        enum ieee80211_band band);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
+void iwl_mvm_dump_sram(struct iwl_mvm *mvm);
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
 
@@ -660,10 +708,26 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
                        u8 flags, bool init);
 
 /* power managment */
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                            struct iwl_powertable_cmd *cmd);
+static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif)
+{
+       return mvm->pm_ops->power_update_mode(mvm, vif);
+}
+
+static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif)
+{
+       return mvm->pm_ops->power_disable(mvm, vif);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif,
+                                           char *buf, int bufsz)
+{
+       return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz);
+}
+#endif
 
 int iwl_mvm_leds_init(struct iwl_mvm *mvm);
 void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
@@ -707,6 +771,12 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif);
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+                                  struct iwl_beacon_filter_cmd *cmd);
+int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif, bool enable);
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif);
 
 /* SMPS */
 void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index af79a14..2fcc8ef 100644 (file)
@@ -275,6 +275,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(BEACON_NOTIFICATION),
        CMD(BEACON_TEMPLATE_CMD),
        CMD(STATISTICS_NOTIFICATION),
+       CMD(REDUCE_TX_POWER_CMD),
        CMD(TX_ANT_CONFIGURATION_CMD),
        CMD(D3_CONFIG_CMD),
        CMD(PROT_OFFLOAD_CONFIG_CMD),
@@ -301,6 +302,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(MCAST_FILTER_CMD),
        CMD(REPLY_BEACON_FILTERING_CMD),
        CMD(REPLY_THERMAL_MNG_BACKOFF),
+       CMD(MAC_PM_POWER_TABLE),
 };
 #undef CMD
 
@@ -340,6 +342,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->fw = fw;
        mvm->hw = hw;
 
+       mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
+
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
@@ -431,6 +435,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (err)
                goto out_unregister;
 
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)
+               mvm->pm_ops = &pm_mac_ops;
+       else
+               mvm->pm_ops = &pm_legacy_ops;
+
+       memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
+
        return op_mode;
 
  out_unregister:
@@ -638,6 +649,22 @@ static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
        ieee80211_free_txskb(mvm->hw, skb);
 }
 
+struct iwl_mvm_reprobe {
+       struct device *dev;
+       struct work_struct work;
+};
+
+static void iwl_mvm_reprobe_wk(struct work_struct *wk)
+{
+       struct iwl_mvm_reprobe *reprobe;
+
+       reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
+       if (device_reprobe(reprobe->dev))
+               dev_err(reprobe->dev, "reprobe failed!\n");
+       kfree(reprobe);
+       module_put(THIS_MODULE);
+}
+
 static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
 {
        iwl_abort_notification_waits(&mvm->notif_wait);
@@ -649,9 +676,30 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
         * can't recover this since we're already half suspended.
         */
        if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
-       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
-                  iwlwifi_mod_params.restart_fw) {
+               struct iwl_mvm_reprobe *reprobe;
+
+               IWL_ERR(mvm,
+                       "Firmware error during reconfiguration - reprobe!\n");
+
+               /*
+                * get a module reference to avoid doing this while unloading
+                * anyway and to avoid scheduling a work with code that's
+                * being removed.
+                */
+               if (!try_module_get(THIS_MODULE)) {
+                       IWL_ERR(mvm, "Module is being unloaded - abort\n");
+                       return;
+               }
+
+               reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC);
+               if (!reprobe) {
+                       module_put(THIS_MODULE);
+                       return;
+               }
+               reprobe->dev = mvm->trans->dev;
+               INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
+               schedule_work(&reprobe->work);
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
                /*
                 * This is a bit racy, but worst case we tell mac80211 about
                 * a stopped/aborted (sched) scan when that was already done
@@ -669,6 +717,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                        break;
                }
 
+               if (mvm->restart_fw > 0)
+                       mvm->restart_fw--;
                ieee80211_restart_hw(mvm->hw);
        }
 }
@@ -678,6 +728,8 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
        iwl_mvm_dump_nic_error_log(mvm);
+       if (!mvm->restart_fw)
+               iwl_mvm_dump_sram(mvm);
 
        iwl_mvm_nic_restart(mvm);
 }
index e7ca965..21407a3 100644 (file)
@@ -75,8 +75,8 @@
 
 #define POWER_KEEP_ALIVE_PERIOD_SEC    25
 
-static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
-                                         struct iwl_beacon_filter_cmd *cmd)
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+                                  struct iwl_beacon_filter_cmd *cmd)
 {
        int ret;
 
@@ -85,69 +85,110 @@ static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
 
        if (!ret) {
                IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
-                               cmd->ba_enable_beacon_abort);
+                               le32_to_cpu(cmd->ba_enable_beacon_abort));
                IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
-                               cmd->ba_escape_timer);
+                               le32_to_cpu(cmd->ba_escape_timer));
                IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
-                               cmd->bf_debug_flag);
+                               le32_to_cpu(cmd->bf_debug_flag));
                IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
-                               cmd->bf_enable_beacon_filter);
+                               le32_to_cpu(cmd->bf_enable_beacon_filter));
                IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
-                               cmd->bf_energy_delta);
+                               le32_to_cpu(cmd->bf_energy_delta));
                IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
-                               cmd->bf_escape_timer);
+                               le32_to_cpu(cmd->bf_escape_timer));
                IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
-                               cmd->bf_roaming_energy_delta);
+                               le32_to_cpu(cmd->bf_roaming_energy_delta));
                IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
-                               cmd->bf_roaming_state);
-               IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
-                               cmd->bf_temperature_delta);
+                               le32_to_cpu(cmd->bf_roaming_state));
+               IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_threshold));
+               IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_fast_filter));
+               IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+                               le32_to_cpu(cmd->bf_temp_slow_filter));
        }
        return ret;
 }
 
-static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
-                                      struct ieee80211_vif *vif, bool enable)
+static
+void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_beacon_filter_cmd *cmd)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->bss_conf.cqm_rssi_thold) {
+               cmd->bf_energy_delta =
+                       cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);
+               /* fw uses an absolute value for this */
+               cmd->bf_roaming_state =
+                       cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);
+       }
+       cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
+}
+
+int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif, bool enable)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_beacon_filter_cmd cmd = {
                IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = 1,
-               .ba_enable_beacon_abort = enable,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
+               .ba_enable_beacon_abort = cpu_to_le32(enable),
        };
 
-       if (!mvmvif->bf_enabled)
+       if (!mvmvif->bf_data.bf_enabled)
                return 0;
 
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
+               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
+       mvmvif->bf_data.ba_enabled = enable;
+       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
        return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 }
 
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
-                             struct iwl_powertable_cmd *cmd)
+                             struct iwl_mac_power_cmd *cmd)
 {
        IWL_DEBUG_POWER(mvm,
-                       "Sending power table command for power level %d, flags = 0x%X\n",
-                       iwlmvm_mod_params.power_scheme,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd->id_and_color, iwlmvm_mod_params.power_scheme,
                        le16_to_cpu(cmd->flags));
-       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
-
-       if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
-                               le32_to_cpu(cmd->rx_data_timeout));
-               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
-                               le32_to_cpu(cmd->tx_data_timeout));
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
-                       IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
-                                       le32_to_cpu(cmd->skip_dtim_periods));
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
-                                       le32_to_cpu(cmd->lprx_rssi_threshold));
+       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+                       le16_to_cpu(cmd->keep_alive_seconds));
+
+       if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
+               IWL_DEBUG_POWER(mvm, "Disable power management\n");
+               return;
+       }
+
+       IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+                       le32_to_cpu(cmd->rx_data_timeout));
+       IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                       le32_to_cpu(cmd->tx_data_timeout));
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+               IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+                               cmd->skip_dtim_periods);
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+               IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                               cmd->lprx_rssi_threshold);
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
+               IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");
+               IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
+                               le32_to_cpu(cmd->rx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",
+                               le32_to_cpu(cmd->tx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);
+               IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);
+               IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);
        }
 }
 
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                            struct iwl_powertable_cmd *cmd)
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct iwl_mac_power_cmd *cmd)
 {
        struct ieee80211_hw *hw = mvm->hw;
        struct ieee80211_chanctx_conf *chanctx_conf;
@@ -157,20 +198,29 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
+       enum ieee80211_ac_numbers ac;
+       bool tid_found = false;
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       dtimper = hw->conf.ps_dtim_period ?: 1;
 
        /*
         * Regardless of power management state the driver must set
         * keep alive period. FW will use it for sending keep alive NDPs
-        * immediately after association.
+        * immediately after association. Check that keep alive period
+        * is at least 3 * DTIM
         */
-       cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+       cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
        if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-       if (!vif->bss_conf.assoc)
-               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
@@ -186,12 +236,9 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
            (vif->bss_conf.beacon_rate->bitrate == 10 ||
             vif->bss_conf.beacon_rate->bitrate == 60)) {
                cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
+               cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
        }
 
-       dtimper = hw->conf.ps_dtim_period ?: 1;
-
        /* Check if radar detection is required on current channel */
        rcu_read_lock();
        chanctx_conf = rcu_dereference(vif->chanctx_conf);
@@ -207,27 +254,82 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
            (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
             mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
                cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
-               cmd->skip_dtim_periods = cpu_to_le32(3);
+               cmd->skip_dtim_periods = 3;
        }
 
-       /* Check that keep alive period is at least 3 * DTIM */
-       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
-       keep_alive = max_t(int, 3 * dtimper_msec,
-                          MSEC_PER_SEC * cmd->keep_alive_seconds);
-       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
-       cmd->keep_alive_seconds = keep_alive;
-
        if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
-               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+               cmd->rx_data_timeout =
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout =
+                       cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
        } else {
-               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
-               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+               cmd->rx_data_timeout =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+       }
+
+       for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
+               if (!mvmvif->queue_params[ac].uapsd)
+                       continue;
+
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+               cmd->uapsd_ac_flags |= BIT(ac);
+
+               /* QNDP TID - the highest TID with no admission control */
+               if (!tid_found && !mvmvif->queue_params[ac].acm) {
+                       tid_found = true;
+                       switch (ac) {
+                       case IEEE80211_AC_VO:
+                               cmd->qndp_tid = 6;
+                               break;
+                       case IEEE80211_AC_VI:
+                               cmd->qndp_tid = 5;
+                               break;
+                       case IEEE80211_AC_BE:
+                               cmd->qndp_tid = 0;
+                               break;
+                       case IEEE80211_AC_BK:
+                               cmd->qndp_tid = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
+               cmd->rx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+
+               if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
+                                           BIT(IEEE80211_AC_VI) |
+                                           BIT(IEEE80211_AC_BE) |
+                                           BIT(IEEE80211_AC_BK))) {
+                       cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+                       cmd->snooze_interval =
+                               cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
+                       cmd->snooze_window =
+                               (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
+                               cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
+                               cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
+               }
+
+               cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
+               cmd->heavy_tx_thld_packets =
+                       IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+               cmd->heavy_rx_thld_packets =
+                       IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+               cmd->heavy_tx_thld_percentage =
+                       IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
+               cmd->heavy_rx_thld_percentage =
+                       IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
        }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
-               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+               cmd->keep_alive_seconds =
+                       cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
                if (mvmvif->dbgfs_pm.skip_over_dtim)
                        cmd->flags |=
@@ -243,8 +345,7 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                cmd->tx_data_timeout =
                        cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
-               cmd->skip_dtim_periods =
-                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+               cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
                if (mvmvif->dbgfs_pm.lprx_ena)
                        cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
@@ -252,16 +353,24 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                        cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
        }
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
-               cmd->lprx_rssi_threshold =
-                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
+               cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
+               if (mvmvif->dbgfs_pm.snooze_ena)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+               else
+                       cmd->flags &=
+                               cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
+       }
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif)
 {
        int ret;
        bool ba_enable;
-       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mac_power_cmd cmd = {};
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
@@ -280,7 +389,7 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
                                   sizeof(cmd), &cmd);
        if (ret)
                return ret;
@@ -291,15 +400,19 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
 }
 
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif)
 {
-       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mac_power_cmd cmd = {};
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
 
        if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                          mvmvif->color));
+
        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
                cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
@@ -310,11 +423,98 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 #endif
        iwl_mvm_power_log(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
                                    sizeof(cmd), &cmd);
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif, char *buf,
+                                       int bufsz)
+{
+       struct iwl_mac_power_cmd cmd = {};
+       int pos = 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                        0 : 1);
+       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+                        iwlmvm_mod_params.power_scheme);
+       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+                        le16_to_cpu(cmd.flags));
+       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+                        le16_to_cpu(cmd.keep_alive_seconds));
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+                                1 : 0);
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                                cmd.skip_dtim_periods);
+               if (!(cmd.flags &
+                     cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "rx_data_timeout = %d\n",
+                                        le32_to_cpu(cmd.rx_data_timeout));
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "tx_data_timeout = %d\n",
+                                        le32_to_cpu(cmd.tx_data_timeout));
+               }
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "lprx_rssi_threshold = %d\n",
+                                        cmd.lprx_rssi_threshold);
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
+                       pos +=
+                       scnprintf(buf+pos, bufsz-pos,
+                                 "rx_data_timeout_uapsd = %d\n",
+                                 le32_to_cpu(cmd.rx_data_timeout_uapsd));
+                       pos +=
+                       scnprintf(buf+pos, bufsz-pos,
+                                 "tx_data_timeout_uapsd = %d\n",
+                                 le32_to_cpu(cmd.tx_data_timeout_uapsd));
+                       pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n",
+                                        cmd.qndp_tid);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "uapsd_ac_flags = 0x%x\n",
+                                        cmd.uapsd_ac_flags);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "uapsd_max_sp = %d\n",
+                                        cmd.uapsd_max_sp);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "heavy_tx_thld_packets = %d\n",
+                                        cmd.heavy_tx_thld_packets);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "heavy_rx_thld_packets = %d\n",
+                                        cmd.heavy_rx_thld_packets);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "heavy_tx_thld_percentage = %d\n",
+                                        cmd.heavy_tx_thld_percentage);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "heavy_rx_thld_percentage = %d\n",
+                                        cmd.heavy_rx_thld_percentage);
+                       pos +=
+                       scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n",
+                                 (cmd.flags &
+                                  cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ?
+                                 1 : 0);
+               }
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "snooze_interval = %d\n",
+                                        cmd.snooze_interval);
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "snooze_window = %d\n",
+                                        cmd.snooze_window);
+               }
+       }
+       return pos;
+}
+
 void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd)
@@ -323,22 +523,30 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
        struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
 
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
-               cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
+               cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
                cmd->bf_roaming_energy_delta =
-                                dbgfs_bf->bf_roaming_energy_delta;
+                               cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
-               cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
-       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
-               cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
+               cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
+               cmd->bf_temp_threshold =
+                               cpu_to_le32(dbgfs_bf->bf_temp_threshold);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
+               cmd->bf_temp_fast_filter =
+                               cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
+       if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
+               cmd->bf_temp_slow_filter =
+                               cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
-               cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
+               cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
                cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
                cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
        if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
-               cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
+               cmd->ba_enable_beacon_abort =
+                               cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
 }
 #endif
 
@@ -348,7 +556,7 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_beacon_filter_cmd cmd = {
                IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = 1,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
        };
        int ret;
 
@@ -356,11 +564,12 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
+       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 
        if (!ret)
-               mvmvif->bf_enabled = true;
+               mvmvif->bf_data.bf_enabled = true;
 
        return ret;
 }
@@ -372,13 +581,33 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
+           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
 
        if (!ret)
-               mvmvif->bf_enabled = false;
+               mvmvif->bf_data.bf_enabled = false;
 
        return ret;
 }
+
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!mvmvif->bf_data.bf_enabled)
+               return 0;
+
+       return iwl_mvm_enable_beacon_filter(mvm, vif);
+}
+
+const struct iwl_mvm_power_ops pm_mac_ops = {
+       .power_update_mode = iwl_mvm_power_mac_update_mode,
+       .power_disable = iwl_mvm_power_mac_disable,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+#endif
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c
new file mode 100644 (file)
index 0000000..2ce79ba
--- /dev/null
@@ -0,0 +1,319 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "mvm.h"
+#include "iwl-modparams.h"
+#include "fw-api-power.h"
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC    25
+
+static void iwl_mvm_power_log(struct iwl_mvm *mvm,
+                             struct iwl_powertable_cmd *cmd)
+{
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command for power level %d, flags = 0x%X\n",
+                       iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd->flags));
+       IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds);
+
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+                               le32_to_cpu(cmd->rx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd->tx_data_timeout));
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
+                       IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
+                                       le32_to_cpu(cmd->skip_dtim_periods));
+               if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                                       le32_to_cpu(cmd->lprx_rssi_threshold));
+       }
+}
+
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct iwl_powertable_cmd *cmd)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *chan;
+       int dtimper, dtimper_msec;
+       int keep_alive;
+       bool radar_detect = false;
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
+
+       /*
+        * Regardless of power management state the driver must set
+        * keep alive period. FW will use it for sending keep alive NDPs
+        * immediately after association.
+        */
+       cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       if (!vif->bss_conf.assoc)
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       if (!vif->bss_conf.ps)
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+       if (vif->bss_conf.beacon_rate &&
+           (vif->bss_conf.beacon_rate->bitrate == 10 ||
+            vif->bss_conf.beacon_rate->bitrate == 60)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD);
+       }
+
+       dtimper = hw->conf.ps_dtim_period ?: 1;
+
+       /* Check if radar detection is required on current channel */
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       WARN_ON(!chanctx_conf);
+       if (chanctx_conf) {
+               chan = chanctx_conf->def.chan;
+               radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
+       }
+       rcu_read_unlock();
+
+       /* Check skip over DTIM conditions */
+       if (!radar_detect && (dtimper <= 10) &&
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
+            mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               cmd->skip_dtim_periods = cpu_to_le32(3);
+       }
+
+       /* Check that keep alive period is at least 3 * DTIM */
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * cmd->keep_alive_seconds);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+       cmd->keep_alive_seconds = keep_alive;
+
+       if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
+               cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC);
+       } else {
+               cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+               cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+       }
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
+               cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
+               if (mvmvif->dbgfs_pm.skip_over_dtim)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               else
+                       cmd->flags &=
+                               cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
+               cmd->rx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
+               cmd->tx_data_timeout =
+                       cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
+               cmd->skip_dtim_periods =
+                       cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods);
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
+               if (mvmvif->dbgfs_pm.lprx_ena)
+                       cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+               else
+                       cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
+       }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
+               cmd->lprx_rssi_threshold =
+                       cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold);
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+}
+
+static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm,
+                                           struct ieee80211_vif *vif)
+{
+       int ret;
+       bool ba_enable;
+       struct iwl_powertable_cmd cmd = {};
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       /*
+        * TODO: The following vif_count verification is temporary condition.
+        * Avoid power mode update if more than one interface is currently
+        * active. Remove this condition when FW will support power management
+        * on multiple MACs.
+        */
+       IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
+                       mvm->vif_count);
+       if (mvm->vif_count > 1)
+               return 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       iwl_mvm_power_log(mvm, &cmd);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               return ret;
+
+       ba_enable = !!(cmd.flags &
+                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
+
+       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
+}
+
+static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif __maybe_unused =
+               iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+               cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
+           mvmvif->dbgfs_pm.disable_power_off)
+               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+#endif
+       iwl_mvm_power_log(mvm, &cmd);
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif, char *buf,
+                                          int bufsz)
+{
+       struct iwl_powertable_cmd cmd = {};
+       int pos = 0;
+
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ?
+                        0 : 1);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        le32_to_cpu(cmd.skip_dtim_periods));
+       pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
+                        iwlmvm_mod_params.power_scheme);
+       pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
+                        le16_to_cpu(cmd.flags));
+       pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
+                        cmd.keep_alive_seconds);
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                                (cmd.flags &
+                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
+                                1 : 0);
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
+               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+                       pos += scnprintf(buf+pos, bufsz-pos,
+                                        "lprx_rssi_threshold = %d\n",
+                                        le32_to_cpu(cmd.lprx_rssi_threshold));
+       }
+       return pos;
+}
+#endif
+
+const struct iwl_mvm_power_ops pm_legacy_ops = {
+       .power_update_mode = iwl_mvm_power_legacy_update_mode,
+       .power_disable = iwl_mvm_power_legacy_disable,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read,
+#endif
+};
index 29d49cf..5c6ae16 100644 (file)
@@ -131,23 +131,22 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
 
 int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 {
-       struct iwl_time_quota_cmd cmd;
-       int i, idx, ret, num_active_bindings, quota, quota_rem;
+       struct iwl_time_quota_cmd cmd = {};
+       int i, idx, ret, num_active_macs, quota, quota_rem;
        struct iwl_mvm_quota_iterator_data data = {
                .n_interfaces = {},
                .colors = { -1, -1, -1, -1 },
                .new_vif = newvif,
        };
 
+       lockdep_assert_held(&mvm->mutex);
+
        /* update all upon completion */
        if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
                return 0;
 
-       BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1);
-
-       lockdep_assert_held(&mvm->mutex);
-
-       memset(&cmd, 0, sizeof(cmd));
+       /* iterator data above must match */
+       BUILD_BUG_ON(MAX_BINDINGS != 4);
 
        ieee80211_iterate_active_interfaces_atomic(
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@@ -162,18 +161,17 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
         * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
         * equally between all the bindings that require quota
         */
-       num_active_bindings = 0;
+       num_active_macs = 0;
        for (i = 0; i < MAX_BINDINGS; i++) {
                cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
-               if (data.n_interfaces[i] > 0)
-                       num_active_bindings++;
+               num_active_macs += data.n_interfaces[i];
        }
 
        quota = 0;
        quota_rem = 0;
-       if (num_active_bindings) {
-               quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
-               quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+       if (num_active_macs) {
+               quota = IWL_MVM_MAX_QUOTA / num_active_macs;
+               quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
        }
 
        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
@@ -187,7 +185,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
                        cmd.quotas[idx].quota = cpu_to_le32(0);
                        cmd.quotas[idx].max_duration = cpu_to_le32(0);
                } else {
-                       cmd.quotas[idx].quota = cpu_to_le32(quota);
+                       cmd.quotas[idx].quota =
+                               cpu_to_le32(quota * data.n_interfaces[i]);
                        cmd.quotas[idx].max_duration =
                                cpu_to_le32(IWL_MVM_MAX_QUOTA);
                }
index b328a98..4ffaa3f 100644 (file)
 #define IWL_RATE_SCALE_FLUSH_INTVL   (3*HZ)
 
 static u8 rs_ht_to_legacy[] = {
-       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
-       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
-       IWL_RATE_6M_INDEX,
-       IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
-       IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
-       IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
-       IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+       [IWL_RATE_1M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_2M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_5M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_11M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_6M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_9M_INDEX] = IWL_RATE_6M_INDEX,
+       [IWL_RATE_12M_INDEX] = IWL_RATE_9M_INDEX,
+       [IWL_RATE_18M_INDEX] = IWL_RATE_12M_INDEX,
+       [IWL_RATE_24M_INDEX] = IWL_RATE_18M_INDEX,
+       [IWL_RATE_36M_INDEX] = IWL_RATE_24M_INDEX,
+       [IWL_RATE_48M_INDEX] = IWL_RATE_36M_INDEX,
+       [IWL_RATE_54M_INDEX] = IWL_RATE_48M_INDEX,
+       [IWL_RATE_60M_INDEX] = IWL_RATE_54M_INDEX,
 };
 
 static const u8 ant_toggle_lookup[] = {
-       /*ANT_NONE -> */ ANT_NONE,
-       /*ANT_A    -> */ ANT_B,
-       /*ANT_B    -> */ ANT_C,
-       /*ANT_AB   -> */ ANT_BC,
-       /*ANT_C    -> */ ANT_A,
-       /*ANT_AC   -> */ ANT_AB,
-       /*ANT_BC   -> */ ANT_AC,
-       /*ANT_ABC  -> */ ANT_ABC,
+       [ANT_NONE] = ANT_NONE,
+       [ANT_A] = ANT_B,
+       [ANT_B] = ANT_C,
+       [ANT_AB] = ANT_BC,
+       [ANT_C] = ANT_A,
+       [ANT_AC] = ANT_AB,
+       [ANT_BC] = ANT_AC,
+       [ANT_ABC] = ANT_ABC,
 };
 
-#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np)    \
+#define IWL_DECLARE_RATE_INFO(r, s, rp, rn)                   \
        [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
                                    IWL_RATE_SISO_##s##M_PLCP, \
                                    IWL_RATE_MIMO2_##s##M_PLCP,\
-                                   IWL_RATE_MIMO3_##s##M_PLCP,\
-                                   IWL_RATE_##r##M_IEEE,      \
-                                   IWL_RATE_##ip##M_INDEX,    \
-                                   IWL_RATE_##in##M_INDEX,    \
                                    IWL_RATE_##rp##M_INDEX,    \
-                                   IWL_RATE_##rn##M_INDEX,    \
-                                   IWL_RATE_##pp##M_INDEX,    \
-                                   IWL_RATE_##np##M_INDEX }
+                                   IWL_RATE_##rn##M_INDEX }
 
 /*
  * Parameter order:
- *   rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *   rate, ht rate, prev rate, next rate
  *
  * If there isn't a valid next or previous rate then INV is used which
  * maps to IWL_RATE_INVALID
  *
  */
 static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
-       IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2),    /*  1mbps */
-       IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5),          /*  2mbps */
-       IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
-       IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18),      /* 11mbps */
-       IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
-       IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11),       /*  9mbps */
-       IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
-       IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
-       IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
-       IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
-       IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
-       IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
-       IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+       IWL_DECLARE_RATE_INFO(1, INV, INV, 2),   /*  1mbps */
+       IWL_DECLARE_RATE_INFO(2, INV, 1, 5),     /*  2mbps */
+       IWL_DECLARE_RATE_INFO(5, INV, 2, 11),    /*5.5mbps */
+       IWL_DECLARE_RATE_INFO(11, INV, 9, 12),   /* 11mbps */
+       IWL_DECLARE_RATE_INFO(6, 6, 5, 11),      /*  6mbps */
+       IWL_DECLARE_RATE_INFO(9, 6, 6, 11),      /*  9mbps */
+       IWL_DECLARE_RATE_INFO(12, 12, 11, 18),   /* 12mbps */
+       IWL_DECLARE_RATE_INFO(18, 18, 12, 24),   /* 18mbps */
+       IWL_DECLARE_RATE_INFO(24, 24, 18, 36),   /* 24mbps */
+       IWL_DECLARE_RATE_INFO(36, 36, 24, 48),   /* 36mbps */
+       IWL_DECLARE_RATE_INFO(48, 48, 36, 54),   /* 48mbps */
+       IWL_DECLARE_RATE_INFO(54, 54, 48, INV),  /* 54mbps */
+       IWL_DECLARE_RATE_INFO(60, 60, 48, INV),  /* 60mbps */
        /* FIXME:RS:          ^^    should be INV (legacy) */
 };
 
@@ -128,9 +128,8 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
        if (rate_n_flags & RATE_MCS_HT_MSK) {
                idx = rs_extract_rate(rate_n_flags);
 
-               if (idx >= IWL_RATE_MIMO3_6M_PLCP)
-                       idx = idx - IWL_RATE_MIMO3_6M_PLCP;
-               else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+               WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP);
+               if (idx >= IWL_RATE_MIMO2_6M_PLCP)
                        idx = idx - IWL_RATE_MIMO2_6M_PLCP;
 
                idx += IWL_FIRST_OFDM_RATE;
@@ -162,10 +161,10 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags, int index);
+                            u32 *rate_n_flags);
 #else
 static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags, int index)
+                            u32 *rate_n_flags)
 {}
 #endif
 
@@ -212,20 +211,6 @@ static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
        {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
 };
 
-static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0,  99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
-       {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
-       {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
-       {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
-};
-
-static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
-       {0, 0, 0, 0, 152, 0, 211, 239, 255, 279,  290,  294,  297}, /* Norm */
-       {0, 0, 0, 0, 160, 0, 219, 245, 261, 284,  294,  297,  300}, /* SGI */
-       {0, 0, 0, 0, 254, 0, 443, 584, 695, 868,  984, 1030, 1070}, /* AGG */
-       {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
-};
-
 /* mbps, mcs */
 static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
        {  "1", "BPSK DSSS"},
@@ -260,82 +245,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
        return (ant_type & valid_antenna) == ant_type;
 }
 
-/*
- *     removes the old data from the statistics. All data that is older than
- *     TID_MAX_TIME_DIFF, will be deleted.
- */
-static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
-{
-       /* The oldest age we want to keep */
-       u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
-
-       while (tl->queue_count &&
-              (tl->time_stamp < oldest_time)) {
-               tl->total -= tl->packet_count[tl->head];
-               tl->packet_count[tl->head] = 0;
-               tl->time_stamp += TID_QUEUE_CELL_SPACING;
-               tl->queue_count--;
-               tl->head++;
-               if (tl->head >= TID_QUEUE_MAX_SIZE)
-                       tl->head = 0;
-       }
-}
-
-/*
- *     increment traffic load value for tid and also remove
- *     any old values if passed the certain time period
- */
-static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
-                          struct ieee80211_hdr *hdr)
-{
-       u32 curr_time = jiffies_to_msecs(jiffies);
-       u32 time_diff;
-       s32 index;
-       struct iwl_traffic_load *tl = NULL;
-       u8 tid;
-
-       if (ieee80211_is_data_qos(hdr->frame_control)) {
-               u8 *qc = ieee80211_get_qos_ctl(hdr);
-               tid = qc[0] & 0xf;
-       } else {
-               return IWL_MAX_TID_COUNT;
-       }
-
-       if (unlikely(tid >= IWL_MAX_TID_COUNT))
-               return IWL_MAX_TID_COUNT;
-
-       tl = &lq_data->load[tid];
-
-       curr_time -= curr_time % TID_ROUND_VALUE;
-
-       /* Happens only for the first packet. Initialize the data */
-       if (!(tl->queue_count)) {
-               tl->total = 1;
-               tl->time_stamp = curr_time;
-               tl->queue_count = 1;
-               tl->head = 0;
-               tl->packet_count[0] = 1;
-               return IWL_MAX_TID_COUNT;
-       }
-
-       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
-       index = time_diff / TID_QUEUE_CELL_SPACING;
-
-       /* The history is too long: remove data that is older than */
-       /* TID_MAX_TIME_DIFF */
-       if (index >= TID_QUEUE_MAX_SIZE)
-               rs_tl_rm_old_stats(tl, curr_time);
-
-       index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
-       tl->packet_count[index] = tl->packet_count[index] + 1;
-       tl->total = tl->total + 1;
-
-       if ((index + 1) > tl->queue_count)
-               tl->queue_count = index + 1;
-
-       return tid;
-}
-
 #ifdef CONFIG_MAC80211_DEBUGFS
 /**
  * Program the device to use fixed rate for frame transmit
@@ -349,7 +258,6 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
        lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
        lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
        lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
-       lq_sta->active_mimo3_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);
@@ -361,45 +269,11 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
 }
 #endif
 
-/*
-       get the traffic load value for tid
-*/
-static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
-{
-       u32 curr_time = jiffies_to_msecs(jiffies);
-       u32 time_diff;
-       s32 index;
-       struct iwl_traffic_load *tl = NULL;
-
-       if (tid >= IWL_MAX_TID_COUNT)
-               return 0;
-
-       tl = &(lq_data->load[tid]);
-
-       curr_time -= curr_time % TID_ROUND_VALUE;
-
-       if (!(tl->queue_count))
-               return 0;
-
-       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
-       index = time_diff / TID_QUEUE_CELL_SPACING;
-
-       /* The history is too long: remove data that is older than */
-       /* TID_MAX_TIME_DIFF */
-       if (index >= TID_QUEUE_MAX_SIZE)
-               rs_tl_rm_old_stats(tl, curr_time);
-
-       return tl->total;
-}
-
 static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
                                      struct iwl_lq_sta *lq_data, u8 tid,
                                      struct ieee80211_sta *sta)
 {
        int ret = -EAGAIN;
-       u32 load;
-
-       load = rs_tl_get_load(lq_data, tid);
 
        /*
         * Don't create TX aggregation sessions when in high
@@ -563,7 +437,7 @@ static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm,
                else if (is_mimo2(tbl->lq_type))
                        rate_n_flags |= iwl_rates[index].plcp_mimo2;
                else
-                       rate_n_flags |= iwl_rates[index].plcp_mimo3;
+                       WARN_ON_ONCE(1);
        } else {
                IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
        }
@@ -601,7 +475,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
        u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
        u8 mcs;
 
-       memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
+       memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win));
        *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
 
        if (*rate_idx  == IWL_RATE_INVALID) {
@@ -640,12 +514,8 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
                } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
                        if (num_of_ant == 2)
                                tbl->lq_type = LQ_MIMO2;
-               /* MIMO3 */
                } else {
-                       if (num_of_ant == 3) {
-                               tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
-                               tbl->lq_type = LQ_MIMO3;
-                       }
+                       WARN_ON_ONCE(num_of_ant == 3);
                }
        }
        return 0;
@@ -711,10 +581,10 @@ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
        } else {
                if (is_siso(rate_type))
                        return lq_sta->active_siso_rate;
-               else if (is_mimo2(rate_type))
+               else {
+                       WARN_ON_ONCE(!is_mimo2(rate_type));
                        return lq_sta->active_mimo2_rate;
-               else
-                       return lq_sta->active_mimo3_rate;
+               }
        }
 }
 
@@ -1089,7 +959,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        }
 
        /* Choose among many HT tables depending on number of streams
-        * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+        * (SISO/MIMO2), channel width (20/40), SGI, and aggregation
         * status */
        if (is_siso(tbl->lq_type) && !tbl->is_ht40)
                ht_tbl_pointer = expected_tpt_siso20MHz;
@@ -1097,12 +967,10 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
                ht_tbl_pointer = expected_tpt_siso40MHz;
        else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
                ht_tbl_pointer = expected_tpt_mimo2_20MHz;
-       else if (is_mimo2(tbl->lq_type))
+       else {
+               WARN_ON_ONCE(!is_mimo2(tbl->lq_type));
                ht_tbl_pointer = expected_tpt_mimo2_40MHz;
-       else if (is_mimo3(tbl->lq_type) && !tbl->is_ht40)
-               ht_tbl_pointer = expected_tpt_mimo3_20MHz;
-       else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
-               ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+       }
 
        if (!tbl->is_SGI && !lq_sta->is_agg)            /* Normal */
                tbl->expected_tpt = ht_tbl_pointer[0];
@@ -1274,58 +1142,6 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
 }
 
 /*
- * Set up search table for MIMO3
- */
-static int rs_switch_to_mimo3(struct iwl_mvm *mvm,
-                            struct iwl_lq_sta *lq_sta,
-                            struct ieee80211_sta *sta,
-                            struct iwl_scale_tbl_info *tbl, int index)
-{
-       u16 rate_mask;
-       s32 rate;
-       s8 is_green = lq_sta->is_green;
-
-       if (!sta->ht_cap.ht_supported)
-               return -1;
-
-       if (sta->smps_mode == IEEE80211_SMPS_STATIC)
-               return -1;
-
-       /* Need both Tx chains/antennas to support MIMO */
-       if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 3)
-               return -1;
-
-       IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n");
-
-       tbl->lq_type = LQ_MIMO3;
-       tbl->action = 0;
-       tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
-       rate_mask = lq_sta->active_mimo3_rate;
-
-       if (iwl_is_ht40_tx_allowed(sta))
-               tbl->is_ht40 = 1;
-       else
-               tbl->is_ht40 = 0;
-
-       rs_set_expected_tpt_table(lq_sta, tbl);
-
-       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
-
-       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 best rate %d mask %X\n",
-                      rate, rate_mask);
-       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
-               IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n",
-                              rate, rate_mask);
-               return -1;
-       }
-       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
-
-       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
-                      tbl->current_rate, is_green);
-       return 0;
-}
-
-/*
  * Set up search table for SISO
  */
 static int rs_switch_to_siso(struct iwl_mvm *mvm,
@@ -1434,21 +1250,14 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
                        }
 
                        break;
-               case IWL_LEGACY_SWITCH_MIMO2_AB:
-               case IWL_LEGACY_SWITCH_MIMO2_AC:
-               case IWL_LEGACY_SWITCH_MIMO2_BC:
+               case IWL_LEGACY_SWITCH_MIMO2:
                        IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n");
 
                        /* Set up search table to try MIMO */
                        memcpy(search_tbl, tbl, sz);
                        search_tbl->is_SGI = 0;
 
-                       if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
-                               search_tbl->ant_type = ANT_AB;
-                       else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
-                               search_tbl->ant_type = ANT_AC;
-                       else
-                               search_tbl->ant_type = ANT_BC;
+                       search_tbl->ant_type = ANT_AB;
 
                        if (!rs_is_valid_ant(valid_tx_ant,
                                             search_tbl->ant_type))
@@ -1461,30 +1270,11 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
                                goto out;
                        }
                        break;
-
-               case IWL_LEGACY_SWITCH_MIMO3_ABC:
-                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO3\n");
-
-                       /* Set up search table to try MIMO3 */
-                       memcpy(search_tbl, tbl, sz);
-                       search_tbl->is_SGI = 0;
-
-                       search_tbl->ant_type = ANT_ABC;
-
-                       if (!rs_is_valid_ant(valid_tx_ant,
-                                            search_tbl->ant_type))
-                               break;
-
-                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
-                                                search_tbl, index);
-                       if (!ret) {
-                               lq_sta->action_counter = 0;
-                               goto out;
-                       }
-                       break;
+               default:
+                       WARN_ON_ONCE(1);
                }
                tbl->action++;
-               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
                        tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
@@ -1496,7 +1286,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm,
 out:
        lq_sta->search_better_tbl = 1;
        tbl->action++;
-       if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+       if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
                tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
@@ -1531,7 +1321,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
        case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
                /* avoid antenna B unless MIMO */
                if (tbl->action == IWL_SISO_SWITCH_ANTENNA2)
-                       tbl->action = IWL_SISO_SWITCH_MIMO2_AB;
+                       tbl->action = IWL_SISO_SWITCH_MIMO2;
                break;
        case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
        case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
@@ -1573,19 +1363,12 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                goto out;
                        }
                        break;
-               case IWL_SISO_SWITCH_MIMO2_AB:
-               case IWL_SISO_SWITCH_MIMO2_AC:
-               case IWL_SISO_SWITCH_MIMO2_BC:
+               case IWL_SISO_SWITCH_MIMO2:
                        IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n");
                        memcpy(search_tbl, tbl, sz);
                        search_tbl->is_SGI = 0;
 
-                       if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
-                               search_tbl->ant_type = ANT_AB;
-                       else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
-                               search_tbl->ant_type = ANT_AC;
-                       else
-                               search_tbl->ant_type = ANT_BC;
+                       search_tbl->ant_type = ANT_AB;
 
                        if (!rs_is_valid_ant(valid_tx_ant,
                                             search_tbl->ant_type))
@@ -1626,24 +1409,11 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
                                                      index, is_green);
                        update_search_tbl_counter = 1;
                        goto out;
-               case IWL_SISO_SWITCH_MIMO3_ABC:
-                       IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO3\n");
-                       memcpy(search_tbl, tbl, sz);
-                       search_tbl->is_SGI = 0;
-                       search_tbl->ant_type = ANT_ABC;
-
-                       if (!rs_is_valid_ant(valid_tx_ant,
-                                            search_tbl->ant_type))
-                               break;
-
-                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
-                                                search_tbl, index);
-                       if (!ret)
-                               goto out;
-                       break;
+               default:
+                       WARN_ON_ONCE(1);
                }
                tbl->action++;
-               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+               if (tbl->action > IWL_SISO_SWITCH_GI)
                        tbl->action = IWL_SISO_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
@@ -1655,7 +1425,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm,
  out:
        lq_sta->search_better_tbl = 1;
        tbl->action++;
-       if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
+       if (tbl->action > IWL_SISO_SWITCH_GI)
                tbl->action = IWL_SISO_SWITCH_ANTENNA1;
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
@@ -1696,8 +1466,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                break;
        case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
                /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_MIMO2_SWITCH_SISO_B ||
-                   tbl->action == IWL_MIMO2_SWITCH_SISO_C)
+               if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
                        tbl->action = IWL_MIMO2_SWITCH_SISO_A;
                break;
        default:
@@ -1730,7 +1499,6 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                        break;
                case IWL_MIMO2_SWITCH_SISO_A:
                case IWL_MIMO2_SWITCH_SISO_B:
-               case IWL_MIMO2_SWITCH_SISO_C:
                        IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
 
                        /* Set up new search table for SISO */
@@ -1738,10 +1506,8 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
 
                        if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
                                search_tbl->ant_type = ANT_A;
-                       else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
+                       else /* tbl->action == IWL_MIMO2_SWITCH_SISO_B */
                                search_tbl->ant_type = ANT_B;
-                       else
-                               search_tbl->ant_type = ANT_C;
 
                        if (!rs_is_valid_ant(valid_tx_ant,
                                             search_tbl->ant_type))
@@ -1784,26 +1550,11 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
                                                      index, is_green);
                        update_search_tbl_counter = 1;
                        goto out;
-
-               case IWL_MIMO2_SWITCH_MIMO3_ABC:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to MIMO3\n");
-                       memcpy(search_tbl, tbl, sz);
-                       search_tbl->is_SGI = 0;
-                       search_tbl->ant_type = ANT_ABC;
-
-                       if (!rs_is_valid_ant(valid_tx_ant,
-                                            search_tbl->ant_type))
-                               break;
-
-                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
-                                                search_tbl, index);
-                       if (!ret)
-                               goto out;
-
-                       break;
+               default:
+                       WARN_ON_ONCE(1);
                }
                tbl->action++;
-               if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+               if (tbl->action > IWL_MIMO2_SWITCH_GI)
                        tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
@@ -1814,7 +1565,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
  out:
        lq_sta->search_better_tbl = 1;
        tbl->action++;
-       if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+       if (tbl->action > IWL_MIMO2_SWITCH_GI)
                tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
        if (update_search_tbl_counter)
                search_tbl->action = tbl->action;
@@ -1823,171 +1574,6 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
 }
 
 /*
- * Try to switch to new modulation mode from MIMO3
- */
-static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
-                                struct iwl_lq_sta *lq_sta,
-                                struct ieee80211_sta *sta, int index)
-{
-       s8 is_green = lq_sta->is_green;
-       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-       struct iwl_scale_tbl_info *search_tbl =
-                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
-       struct iwl_rate_scale_data *window = &(tbl->win[index]);
-       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
-                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
-       u8 start_action;
-       u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-       u8 tx_chains_num = num_of_ant(valid_tx_ant);
-       int ret;
-       u8 update_search_tbl_counter = 0;
-
-       switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
-       case IWL_BT_COEX_TRAFFIC_LOAD_NONE:
-               /* nothing */
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
-       case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
-               /* avoid antenna B and MIMO */
-               if (tbl->action != IWL_MIMO3_SWITCH_SISO_A)
-                       tbl->action = IWL_MIMO3_SWITCH_SISO_A;
-               break;
-       case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
-               /* avoid antenna B unless MIMO */
-               if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
-                   tbl->action == IWL_MIMO3_SWITCH_SISO_C)
-                       tbl->action = IWL_MIMO3_SWITCH_SISO_A;
-               break;
-       default:
-               IWL_ERR(mvm, "Invalid BT load %d",
-                       BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD));
-               break;
-       }
-
-       start_action = tbl->action;
-       while (1) {
-               lq_sta->action_counter++;
-               switch (tbl->action) {
-               case IWL_MIMO3_SWITCH_ANTENNA1:
-               case IWL_MIMO3_SWITCH_ANTENNA2:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle Antennas\n");
-
-                       if (tx_chains_num <= 3)
-                               break;
-
-                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
-                               break;
-
-                       memcpy(search_tbl, tbl, sz);
-                       if (rs_toggle_antenna(valid_tx_ant,
-                                             &search_tbl->current_rate,
-                                             search_tbl))
-                               goto out;
-                       break;
-               case IWL_MIMO3_SWITCH_SISO_A:
-               case IWL_MIMO3_SWITCH_SISO_B:
-               case IWL_MIMO3_SWITCH_SISO_C:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to SISO\n");
-
-                       /* Set up new search table for SISO */
-                       memcpy(search_tbl, tbl, sz);
-
-                       if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
-                               search_tbl->ant_type = ANT_A;
-                       else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
-                               search_tbl->ant_type = ANT_B;
-                       else
-                               search_tbl->ant_type = ANT_C;
-
-                       if (!rs_is_valid_ant(valid_tx_ant,
-                                            search_tbl->ant_type))
-                               break;
-
-                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
-                                               search_tbl, index);
-                       if (!ret)
-                               goto out;
-
-                       break;
-
-               case IWL_MIMO3_SWITCH_MIMO2_AB:
-               case IWL_MIMO3_SWITCH_MIMO2_AC:
-               case IWL_MIMO3_SWITCH_MIMO2_BC:
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to MIMO2\n");
-
-                       memcpy(search_tbl, tbl, sz);
-                       search_tbl->is_SGI = 0;
-                       if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
-                               search_tbl->ant_type = ANT_AB;
-                       else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
-                               search_tbl->ant_type = ANT_AC;
-                       else
-                               search_tbl->ant_type = ANT_BC;
-
-                       if (!rs_is_valid_ant(valid_tx_ant,
-                                            search_tbl->ant_type))
-                               break;
-
-                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
-                                                search_tbl, index);
-                       if (!ret)
-                               goto out;
-
-                       break;
-
-               case IWL_MIMO3_SWITCH_GI:
-                       if (!tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_20))
-                               break;
-                       if (tbl->is_ht40 && !(ht_cap->cap &
-                                               IEEE80211_HT_CAP_SGI_40))
-                               break;
-
-                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle SGI/NGI\n");
-
-                       /* Set up new search table for MIMO */
-                       memcpy(search_tbl, tbl, sz);
-                       search_tbl->is_SGI = !tbl->is_SGI;
-                       rs_set_expected_tpt_table(lq_sta, search_tbl);
-                       /*
-                        * If active table already uses the fastest possible
-                        * modulation (dual stream with short guard interval),
-                        * and it's working well, there's no need to look
-                        * for a better type of modulation!
-                        */
-                       if (tbl->is_SGI) {
-                               s32 tpt = lq_sta->last_tpt / 100;
-                               if (tpt >= search_tbl->expected_tpt[index])
-                                       break;
-                       }
-                       search_tbl->current_rate =
-                               rate_n_flags_from_tbl(mvm, search_tbl,
-                                                     index, is_green);
-                       update_search_tbl_counter = 1;
-                       goto out;
-               }
-               tbl->action++;
-               if (tbl->action > IWL_MIMO3_SWITCH_GI)
-                       tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
-
-               if (tbl->action == start_action)
-                       break;
-       }
-       search_tbl->lq_type = LQ_NONE;
-       return 0;
- out:
-       lq_sta->search_better_tbl = 1;
-       tbl->action++;
-       if (tbl->action > IWL_MIMO3_SWITCH_GI)
-               tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
-       if (update_search_tbl_counter)
-               search_tbl->action = tbl->action;
-
-       return 0;
-}
-
-/*
  * Check whether we should continue using same modulation mode, or
  * begin search for a new mode, based on:
  * 1) # tx successes or failures while using this mode
@@ -2086,6 +1672,22 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
 }
 
+static u8 rs_get_tid(struct iwl_lq_sta *lq_data,
+                    struct ieee80211_hdr *hdr)
+{
+       u8 tid = IWL_MAX_TID_COUNT;
+
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & 0xf;
+       }
+
+       if (unlikely(tid > IWL_MAX_TID_COUNT))
+               tid = IWL_MAX_TID_COUNT;
+
+       return tid;
+}
+
 /*
  * Do rate scaling and search for new modulation mode.
  */
@@ -2129,7 +1731,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
 
        lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
 
-       tid = rs_tl_add_packet(lq_sta, hdr);
+       tid = rs_get_tid(lq_sta, hdr);
        if ((tid != IWL_MAX_TID_COUNT) &&
            (lq_sta->tx_agg_tid_en & (1 << tid))) {
                tid_data = &sta_priv->tid_data[tid];
@@ -2377,8 +1979,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                scale_action = 0;
 
        if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
-            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
-            (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) {
                if (lq_sta->last_bt_traffic >
                    BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) {
                        /*
@@ -2395,8 +1996,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
 
        if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
-            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
-            (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+            IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) {
                /* search for a new modulation */
                rs_stay_in_table(lq_sta, true);
                goto lq_update;
@@ -2456,7 +2056,7 @@ lq_update:
                else if (is_mimo2(tbl->lq_type))
                        rs_move_mimo2_to_other(mvm, lq_sta, sta, index);
                else
-                       rs_move_mimo3_to_other(mvm, lq_sta, sta, index);
+                       WARN_ON_ONCE(1);
 
                /* If new "search" mode was selected, set up in uCode table */
                if (lq_sta->search_better_tbl) {
@@ -2621,11 +2221,10 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
                rate_idx -= IWL_FIRST_OFDM_RATE;
                /* 6M and 9M shared same MCS index */
                rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
+               WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >=
+                            IWL_RATE_MIMO3_6M_PLCP);
                if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                   IWL_RATE_MIMO3_6M_PLCP)
-                       rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
-               else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
-                        IWL_RATE_MIMO2_6M_PLCP)
+                   IWL_RATE_MIMO2_6M_PLCP)
                        rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
                info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
                if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
@@ -2688,9 +2287,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        lq_sta->flush_timer = 0;
        lq_sta->supp_rates = sta->supp_rates[sband->band];
-       for (j = 0; j < LQ_SIZE; j++)
-               for (i = 0; i < IWL_RATE_COUNT; i++)
-                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        IWL_DEBUG_RATE(mvm,
                       "LQ: *** rate scale station global init for station %d ***\n",
@@ -2727,16 +2323,10 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        lq_sta->active_mimo2_rate &= ~((u16)0x2);
        lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
 
-       lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
-       lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
-       lq_sta->active_mimo3_rate &= ~((u16)0x2);
-       lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
-
        IWL_DEBUG_RATE(mvm,
-                      "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
+                      "SISO-RATE=%X MIMO2-RATE=%X\n",
                       lq_sta->active_siso_rate,
-                      lq_sta->active_mimo2_rate,
-                      lq_sta->active_mimo3_rate);
+                      lq_sta->active_mimo2_rate);
 
        /* These values will be overridden later */
        lq_sta->lq.single_stream_ant_msk =
@@ -2780,7 +2370,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
        struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
 
        /* Override starting rate (index 0) if needed for debug purposes */
-       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+       rs_dbgfs_set_mcs(lq_sta, &new_rate);
 
        /* Interpret new_rate (rate_n_flags) */
        rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
@@ -2827,7 +2417,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                        }
 
                        /* Override next rate if needed for debug purposes */
-                       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+                       rs_dbgfs_set_mcs(lq_sta, &new_rate);
 
                        /* Fill next table entry */
                        lq_cmd->rs_table[index] =
@@ -2869,7 +2459,7 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm,
                use_ht_possible = 0;
 
                /* Override next rate if needed for debug purposes */
-               rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+               rs_dbgfs_set_mcs(lq_sta, &new_rate);
 
                /* Fill next table entry */
                lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
@@ -2914,7 +2504,7 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags, int index)
+                            u32 *rate_n_flags)
 {
        struct iwl_mvm *mvm;
        u8 valid_tx_ant;
@@ -2999,8 +2589,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
           (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
        if (is_Ht(tbl->lq_type)) {
                desc += sprintf(buff+desc, " %s",
-                  (is_siso(tbl->lq_type)) ? "SISO" :
-                  ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
+                  (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2");
                   desc += sprintf(buff+desc, " %s",
                   (tbl->is_ht40) ? "40MHz" : "20MHz");
                   desc += sprintf(buff+desc, " %s %s %s\n",
@@ -3100,32 +2689,6 @@ static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
        .llseek = default_llseek,
 };
 
-static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
-                       char __user *user_buf, size_t count, loff_t *ppos)
-{
-       struct iwl_lq_sta *lq_sta = file->private_data;
-       struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
-       char buff[120];
-       int desc = 0;
-
-       if (is_Ht(tbl->lq_type))
-               desc += sprintf(buff+desc,
-                               "Bit Rate= %d Mb/s\n",
-                               tbl->expected_tpt[lq_sta->last_txrate_idx]);
-       else
-               desc += sprintf(buff+desc,
-                               "Bit Rate= %d Mb/s\n",
-                               iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
-
-       return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
-}
-
-static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
-       .read = rs_sta_dbgfs_rate_scale_data_read,
-       .open = simple_open,
-       .llseek = default_llseek,
-};
-
 static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
 {
        struct iwl_lq_sta *lq_sta = mvm_sta;
@@ -3135,9 +2698,6 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
        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_rate_scale_data_file =
-               debugfs_create_file("rate_scale_data", S_IRUSR, dir,
-                                   lq_sta, &rs_sta_dbgfs_rate_scale_data_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);
@@ -3148,7 +2708,6 @@ 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_rate_scale_data_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
 }
 #endif
@@ -3159,8 +2718,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
  * station is added we ignore it.
  */
 static void rs_rate_init_stub(void *mvm_r,
-                                struct ieee80211_supported_band *sband,
-                                struct ieee80211_sta *sta, void *mvm_sta)
+                             struct ieee80211_supported_band *sband,
+                             struct cfg80211_chan_def *chandef,
+                             struct ieee80211_sta *sta, void *mvm_sta)
 {
 }
 static struct rate_control_ops rs_mvm_ops = {
@@ -3193,13 +2753,14 @@ void iwl_mvm_rate_control_unregister(void)
  * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
  * Tx protection, according to this rquest and previous requests,
  * and send the LQ command.
- * @lq: The LQ command
  * @mvmsta: The station
  * @enable: Enable Tx protection?
  */
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
-                         struct iwl_mvm_sta *mvmsta, bool enable)
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                         bool enable)
 {
+       struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
+
        lockdep_assert_held(&mvm->mutex);
 
        if (enable) {
index cff4f6d..335cf16 100644 (file)
@@ -38,14 +38,8 @@ struct iwl_rs_rate_info {
        u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
        u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
        u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
-       u8 plcp_mimo3;  /* uCode API:  IWL_RATE_MIMO3_6M_PLCP, etc. */
-       u8 ieee;        /* MAC header:  IWL_RATE_6M_IEEE, etc. */
-       u8 prev_ieee;    /* previous rate in IEEE speeds */
-       u8 next_ieee;    /* next rate in IEEE speeds */
        u8 prev_rs;      /* previous rate used in rs algo */
        u8 next_rs;      /* next rate used in rs algo */
-       u8 prev_rs_tgg;  /* previous rate used in TGG rs algo */
-       u8 next_rs_tgg;  /* next rate used in TGG rs algo */
 };
 
 #define IWL_RATE_60M_PLCP 3
@@ -120,23 +114,6 @@ enum {
        IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
 };
 
-/* MAC header values for bit rates */
-enum {
-       IWL_RATE_6M_IEEE  = 12,
-       IWL_RATE_9M_IEEE  = 18,
-       IWL_RATE_12M_IEEE = 24,
-       IWL_RATE_18M_IEEE = 36,
-       IWL_RATE_24M_IEEE = 48,
-       IWL_RATE_36M_IEEE = 72,
-       IWL_RATE_48M_IEEE = 96,
-       IWL_RATE_54M_IEEE = 108,
-       IWL_RATE_60M_IEEE = 120,
-       IWL_RATE_1M_IEEE  = 2,
-       IWL_RATE_2M_IEEE  = 4,
-       IWL_RATE_5M_IEEE  = 11,
-       IWL_RATE_11M_IEEE = 22,
-};
-
 #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
 
 #define IWL_INVALID_VALUE    -1
@@ -165,47 +142,22 @@ enum {
 #define IWL_LEGACY_SWITCH_ANTENNA1      0
 #define IWL_LEGACY_SWITCH_ANTENNA2      1
 #define IWL_LEGACY_SWITCH_SISO          2
-#define IWL_LEGACY_SWITCH_MIMO2_AB      3
-#define IWL_LEGACY_SWITCH_MIMO2_AC      4
-#define IWL_LEGACY_SWITCH_MIMO2_BC      5
-#define IWL_LEGACY_SWITCH_MIMO3_ABC     6
+#define IWL_LEGACY_SWITCH_MIMO2         3
 
 /* possible actions when in siso mode */
 #define IWL_SISO_SWITCH_ANTENNA1        0
 #define IWL_SISO_SWITCH_ANTENNA2        1
-#define IWL_SISO_SWITCH_MIMO2_AB        2
-#define IWL_SISO_SWITCH_MIMO2_AC        3
-#define IWL_SISO_SWITCH_MIMO2_BC        4
-#define IWL_SISO_SWITCH_GI              5
-#define IWL_SISO_SWITCH_MIMO3_ABC       6
-
+#define IWL_SISO_SWITCH_MIMO2           2
+#define IWL_SISO_SWITCH_GI              3
 
 /* possible actions when in mimo mode */
 #define IWL_MIMO2_SWITCH_ANTENNA1       0
 #define IWL_MIMO2_SWITCH_ANTENNA2       1
 #define IWL_MIMO2_SWITCH_SISO_A         2
 #define IWL_MIMO2_SWITCH_SISO_B         3
-#define IWL_MIMO2_SWITCH_SISO_C         4
-#define IWL_MIMO2_SWITCH_GI             5
-#define IWL_MIMO2_SWITCH_MIMO3_ABC      6
-
+#define IWL_MIMO2_SWITCH_GI             4
 
-/* possible actions when in mimo3 mode */
-#define IWL_MIMO3_SWITCH_ANTENNA1       0
-#define IWL_MIMO3_SWITCH_ANTENNA2       1
-#define IWL_MIMO3_SWITCH_SISO_A         2
-#define IWL_MIMO3_SWITCH_SISO_B         3
-#define IWL_MIMO3_SWITCH_SISO_C         4
-#define IWL_MIMO3_SWITCH_MIMO2_AB       5
-#define IWL_MIMO3_SWITCH_MIMO2_AC       6
-#define IWL_MIMO3_SWITCH_MIMO2_BC       7
-#define IWL_MIMO3_SWITCH_GI             8
-
-
-#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
-#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
-
-/*FIXME:RS:add possible actions for MIMO3*/
+#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI
 
 #define IWL_ACTION_LIMIT               3       /* # possible actions */
 
@@ -240,15 +192,13 @@ enum iwl_table_type {
        LQ_A,
        LQ_SISO,        /* high-throughput types */
        LQ_MIMO2,
-       LQ_MIMO3,
        LQ_MAX,
 };
 
 #define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
 #define is_siso(tbl) ((tbl) == LQ_SISO)
 #define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
-#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
-#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
+#define is_mimo(tbl) is_mimo2(tbl)
 #define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
 #define is_a_band(tbl) ((tbl) == LQ_A)
 #define is_g_and(tbl) ((tbl) == LQ_G)
@@ -290,17 +240,6 @@ struct iwl_scale_tbl_info {
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 };
 
-struct iwl_traffic_load {
-       unsigned long time_stamp;       /* age of the oldest statistics */
-       u32 packet_count[TID_QUEUE_MAX_SIZE];   /* packet count in this time
-                                                * slice */
-       u32 total;                      /* total num of packets during the
-                                        * last TID_MAX_TIME_DIFF */
-       u8 queue_count;                 /* number of queues that has
-                                        * been used since the last cleanup */
-       u8 head;                        /* start of the circular buffer */
-};
-
 /**
  * struct iwl_lq_sta -- driver's rate scaling private structure
  *
@@ -331,18 +270,15 @@ struct iwl_lq_sta {
        u16 active_legacy_rate;
        u16 active_siso_rate;
        u16 active_mimo2_rate;
-       u16 active_mimo3_rate;
        s8 max_rate_idx;     /* Max rate set by user */
        u8 missed_rate_counter;
 
        struct iwl_lq_cmd lq;
        struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
-       struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
        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_rate_scale_data_file;
        struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
        u32 dbg_fixed_rate;
 #endif
@@ -404,7 +340,7 @@ extern void iwl_mvm_rate_control_unregister(void);
 
 struct iwl_mvm_sta;
 
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
-                         struct iwl_mvm_sta *mvmsta, bool enable);
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                         bool enable);
 
 #endif /* __rs__ */
index e4930d5..2a8cb5a 100644 (file)
@@ -124,24 +124,15 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
        ieee80211_rx_ni(mvm->hw, skb);
 }
 
-/*
- * iwl_mvm_calc_rssi - calculate the rssi in dBm
- * @phy_info: the phy information for the coming packet
- */
-static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
-                            struct iwl_rx_phy_info *phy_info)
+static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
+                             struct iwl_rx_phy_info *phy_info,
+                             struct ieee80211_rx_status *rx_status)
 {
        int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
        int rssi_all_band_a, rssi_all_band_b;
        u32 agc_a, agc_b, max_agc;
        u32 val;
 
-       /* Find max rssi among 2 possible receivers.
-        * These values are measured by the Digital Signal Processor (DSP).
-        * They should stay fairly constant even as the signal strength varies,
-        * if the radio's Automatic Gain Control (AGC) is working right.
-        * AGC value (see below) will provide the "interesting" info.
-        */
        val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
        agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
        agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
@@ -166,7 +157,51 @@ static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
        IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
                        rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
 
-       return max_rssi_dbm;
+       rx_status->signal = max_rssi_dbm;
+       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+       rx_status->chain_signal[0] = rssi_a_dbm;
+       rx_status->chain_signal[1] = rssi_b_dbm;
+}
+
+/*
+ * iwl_mvm_get_signal_strength - use new rx PHY INFO API
+ * values are reported by the fw as positive values - need to negate
+ * to obtain their dBM.  Account for missing antennas by replacing 0
+ * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
+ */
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+                                       struct iwl_rx_phy_info *phy_info,
+                                       struct ieee80211_rx_status *rx_status)
+{
+       int energy_a, energy_b, energy_c, max_energy;
+       u32 val;
+
+       val =
+           le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
+       energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_A_POS;
+       energy_a = energy_a ? -energy_a : -256;
+       energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_B_POS;
+       energy_b = energy_b ? -energy_b : -256;
+       energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
+                                               IWL_RX_INFO_ENERGY_ANT_C_POS;
+       energy_c = energy_c ? -energy_c : -256;
+       max_energy = max(energy_a, energy_b);
+       max_energy = max(max_energy, energy_c);
+
+       IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+                       energy_a, energy_b, energy_c, max_energy);
+
+       rx_status->signal = max_energy;
+       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+       rx_status->chain_signal[0] = energy_a;
+       rx_status->chain_signal[1] = energy_b;
+       rx_status->chain_signal[2] = energy_c;
 }
 
 /*
@@ -289,29 +324,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
         */
        /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
 
-       /* Find max signal strength (dBm) among 3 antenna/receiver chains */
-       rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API)
+               iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
+       else
+               iwl_mvm_calc_rssi(mvm, phy_info, &rx_status);
 
        IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
                              (unsigned long long)rx_status.mactime);
 
-       /*
-        * "antenna number"
-        *
-        * It seems that the antenna field in the phy flags value
-        * is actually a bit field. This is undefined by radiotap,
-        * it wants an actual antenna number but I always get "7"
-        * for most legacy frames I receive indicating that the
-        * same frame was received on all three RX chains.
-        *
-        * I think this field should be removed in favor of a
-        * new 802.11n radiotap field "RX chains" that is defined
-        * as a bitmask.
-        */
-       rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
-                               RX_RES_PHY_FLAGS_ANTENNA)
-                               >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-
        /* set the preamble flag if appropriate */
        if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
                rx_status.flag |= RX_FLAG_SHORTPRE;
@@ -364,11 +384,74 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        return 0;
 }
 
+static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
+                                        struct iwl_notif_statistics *stats)
+{
+       /*
+        * NOTE FW aggregates the statistics - BUT the statistics are cleared
+        * when the driver issues REPLY_STATISTICS_CMD 0x9c with CLEAR_STATS
+        * bit set.
+        */
+       lockdep_assert_held(&mvm->mutex);
+       memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx));
+}
+
+struct iwl_mvm_stat_data {
+       struct iwl_notif_statistics *stats;
+       struct iwl_mvm *mvm;
+};
+
+static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
+                                 struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_stat_data *data = _data;
+       struct iwl_notif_statistics *stats = data->stats;
+       struct iwl_mvm *mvm = data->mvm;
+       int sig = -stats->general.beacon_filter_average_energy;
+       int last_event;
+       int thold = vif->bss_conf.cqm_rssi_thold;
+       int hyst = vif->bss_conf.cqm_rssi_hyst;
+       u16 id = le32_to_cpu(stats->rx.general.mac_id);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (mvmvif->id != id)
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       mvmvif->bf_data.ave_beacon_signal = sig;
+
+       if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+               return;
+
+       /* CQM Notification */
+       last_event = mvmvif->bf_data.last_cqm_event;
+       if (thold && sig < thold && (last_event == 0 ||
+                                    sig < last_event - hyst)) {
+               mvmvif->bf_data.last_cqm_event = sig;
+               IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
+                            sig);
+               ieee80211_cqm_rssi_notify(
+                       vif,
+                       NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                       GFP_KERNEL);
+       } else if (sig > thold &&
+                  (last_event == 0 || sig > last_event + hyst)) {
+               mvmvif->bf_data.last_cqm_event = sig;
+               IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
+                            sig);
+               ieee80211_cqm_rssi_notify(
+                       vif,
+                       NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                       GFP_KERNEL);
+       }
+}
+
 /*
  * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
  *
  * TODO: This handler is implemented partially.
- * It only gets the NIC's temperature.
  */
 int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
                          struct iwl_rx_cmd_buffer *rxb,
@@ -377,11 +460,20 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_notif_statistics *stats = (void *)&pkt->data;
        struct mvm_statistics_general_common *common = &stats->general.common;
+       struct iwl_mvm_stat_data data = {
+               .stats = stats,
+               .mvm = mvm,
+       };
 
        if (mvm->temperature != le32_to_cpu(common->temperature)) {
                mvm->temperature = le32_to_cpu(common->temperature);
                iwl_mvm_tt_handler(mvm);
        }
+       iwl_mvm_update_rx_statistics(mvm, stats);
 
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_stat_iterator,
+                                           &data);
        return 0;
 }
index acdff6b..9a7ab84 100644 (file)
@@ -301,10 +301,12 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
         */
        if (req->n_ssids > 0) {
                cmd->passive2active = cpu_to_le16(1);
+               cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
                ssid = req->ssids[0].ssid;
                ssid_len = req->ssids[0].ssid_len;
        } else {
                cmd->passive2active = 0;
+               cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
        iwl_mvm_scan_fill_ssids(cmd, req);
index 563f559..44add29 100644 (file)
@@ -826,8 +826,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                 * method for HT traffic
                 * this function also sends the LQ command
                 */
-               return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
-                                            mvmsta, true);
+               return iwl_mvm_tx_protection(mvm, mvmsta, true);
                /*
                 * TODO: remove the TLC_RTS flag when we tear down the last
                 * AGG session (agg_tids_count in DVM)
index 7fd6fbf..c17b74c 100644 (file)
@@ -73,7 +73,6 @@
 #include "iwl-prph.h"
 
 /* A TimeUnit is 1024 microsecond */
-#define TU_TO_JIFFIES(_tu)     (usecs_to_jiffies((_tu) * 1024))
 #define MSEC_TO_TU(_msec)      (_msec*1000/1024)
 
 /*
@@ -185,7 +184,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                }
        }
 
-       if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
+       if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
                IWL_DEBUG_TE(mvm,
                             "TE ended - current time %lu, estimated end %lu\n",
                             jiffies, te_data->end_jiffies);
@@ -202,10 +201,9 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                iwl_mvm_te_check_disconnect(mvm, te_data->vif,
                        "No assocation and the time event is over already...");
                iwl_mvm_te_clear_data(mvm, te_data);
-       } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
+       } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
                te_data->running = true;
-               te_data->end_jiffies = jiffies +
-                       TU_TO_JIFFIES(te_data->duration);
+               te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
 
                if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
                        set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
@@ -270,10 +268,67 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
        return true;
 }
 
+/* used to convert from time event API v2 to v1 */
+#define TE_V2_DEP_POLICY_MSK (TE_V2_DEP_OTHER | TE_V2_DEP_TSF |\
+                            TE_V2_EVENT_SOCIOPATHIC)
+static inline u16 te_v2_get_notify(__le16 policy)
+{
+       return le16_to_cpu(policy) & TE_V2_NOTIF_MSK;
+}
+
+static inline u16 te_v2_get_dep_policy(__le16 policy)
+{
+       return (le16_to_cpu(policy) & TE_V2_DEP_POLICY_MSK) >>
+               TE_V2_PLACEMENT_POS;
+}
+
+static inline u16 te_v2_get_absence(__le16 policy)
+{
+       return (le16_to_cpu(policy) & TE_V2_ABSENCE) >> TE_V2_ABSENCE_POS;
+}
+
+static void iwl_mvm_te_v2_to_v1(const struct iwl_time_event_cmd_v2 *cmd_v2,
+                               struct iwl_time_event_cmd_v1 *cmd_v1)
+{
+       cmd_v1->id_and_color = cmd_v2->id_and_color;
+       cmd_v1->action = cmd_v2->action;
+       cmd_v1->id = cmd_v2->id;
+       cmd_v1->apply_time = cmd_v2->apply_time;
+       cmd_v1->max_delay = cmd_v2->max_delay;
+       cmd_v1->depends_on = cmd_v2->depends_on;
+       cmd_v1->interval = cmd_v2->interval;
+       cmd_v1->duration = cmd_v2->duration;
+       if (cmd_v2->repeat == TE_V2_REPEAT_ENDLESS)
+               cmd_v1->repeat = cpu_to_le32(TE_V1_REPEAT_ENDLESS);
+       else
+               cmd_v1->repeat = cpu_to_le32(cmd_v2->repeat);
+       cmd_v1->max_frags = cpu_to_le32(cmd_v2->max_frags);
+       cmd_v1->interval_reciprocal = 0; /* unused */
+
+       cmd_v1->dep_policy = cpu_to_le32(te_v2_get_dep_policy(cmd_v2->policy));
+       cmd_v1->is_present = cpu_to_le32(!te_v2_get_absence(cmd_v2->policy));
+       cmd_v1->notify = cpu_to_le32(te_v2_get_notify(cmd_v2->policy));
+}
+
+static int iwl_mvm_send_time_event_cmd(struct iwl_mvm *mvm,
+                                      const struct iwl_time_event_cmd_v2 *cmd)
+{
+       struct iwl_time_event_cmd_v1 cmd_v1;
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2)
+               return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                           sizeof(*cmd), cmd);
+
+       iwl_mvm_te_v2_to_v1(cmd, &cmd_v1);
+       return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                   sizeof(cmd_v1), &cmd_v1);
+}
+
+
 static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif,
                                       struct iwl_mvm_time_event_data *te_data,
-                                      struct iwl_time_event_cmd *te_cmd)
+                                      struct iwl_time_event_cmd_v2 *te_cmd)
 {
        static const u8 time_event_response[] = { TIME_EVENT_CMD };
        struct iwl_notification_wait wait_time_event;
@@ -309,8 +364,7 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                   ARRAY_SIZE(time_event_response),
                                   iwl_mvm_time_event_response, te_data);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                  sizeof(*te_cmd), te_cmd);
+       ret = iwl_mvm_send_time_event_cmd(mvm, te_cmd);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
                iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
@@ -337,13 +391,12 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
 {
        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 = {};
+       struct iwl_time_event_cmd_v2 time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
 
        if (te_data->running &&
-           time_after(te_data->end_jiffies,
-                      jiffies + TU_TO_JIFFIES(min_duration))) {
+           time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
                IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
                             jiffies_to_msecs(te_data->end_jiffies - jiffies));
                return;
@@ -372,17 +425,14 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
        time_cmd.apply_time =
                cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
 
-       time_cmd.dep_policy = TE_INDEPENDENT;
-       time_cmd.is_present = cpu_to_le32(1);
-       time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE);
+       time_cmd.max_frags = TE_V2_FRAG_NONE;
        time_cmd.max_delay = cpu_to_le32(500);
        /* TODO: why do we need to interval = bi if it is not periodic? */
        time_cmd.interval = cpu_to_le32(1);
-       time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
        time_cmd.duration = cpu_to_le32(duration);
-       time_cmd.repeat = cpu_to_le32(1);
-       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
-                                     TE_NOTIF_HOST_EVENT_END);
+       time_cmd.repeat = 1;
+       time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+                                     TE_V2_NOTIF_HOST_EVENT_END);
 
        iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 }
@@ -396,7 +446,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                               struct iwl_mvm_vif *mvmvif,
                               struct iwl_mvm_time_event_data *te_data)
 {
-       struct iwl_time_event_cmd time_cmd = {};
+       struct iwl_time_event_cmd_v2 time_cmd = {};
        u32 id, uid;
        int ret;
 
@@ -433,8 +483,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 
        IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                  sizeof(time_cmd), &time_cmd);
+       ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd);
        if (WARN_ON(ret))
                return;
 }
@@ -454,7 +503,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 {
        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 = {};
+       struct iwl_time_event_cmd_v2 time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
        if (te_data->running) {
@@ -485,8 +534,6 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        }
 
        time_cmd.apply_time = cpu_to_le32(0);
-       time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
-       time_cmd.is_present = cpu_to_le32(1);
        time_cmd.interval = cpu_to_le32(1);
 
        /*
@@ -495,12 +542,12 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
         * scheduled. To improve the chances of it being scheduled, allow them
         * to be fragmented, and in addition allow them to be delayed.
         */
-       time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
+       time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
        time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
        time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
-       time_cmd.repeat = cpu_to_le32(1);
-       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
-                                     TE_NOTIF_HOST_EVENT_END);
+       time_cmd.repeat = 1;
+       time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+                                     TE_V2_NOTIF_HOST_EVENT_END);
 
        return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
 }
index d6ae7f1..1f3282d 100644 (file)
@@ -391,8 +391,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
                mvmsta = (void *)sta->drv_priv;
                if (enable == mvmsta->tt_tx_protection)
                        continue;
-               err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
-                                           mvmsta, enable);
+               err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
                if (err) {
                        IWL_ERR(mvm, "Failed to %s Tx protection\n",
                                enable ? "enable" : "disable");
@@ -513,12 +512,39 @@ static const struct iwl_tt_params iwl7000_tt_params = {
        .support_tx_backoff = true,
 };
 
+static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
+       .ct_kill_entry = 118,
+       .ct_kill_exit = 96,
+       .ct_kill_duration = 5,
+       .dynamic_smps_entry = 114,
+       .dynamic_smps_exit = 110,
+       .tx_protection_entry = 114,
+       .tx_protection_exit = 108,
+       .tx_backoff = {
+               {.temperature = 112, .backoff = 300},
+               {.temperature = 113, .backoff = 800},
+               {.temperature = 114, .backoff = 1500},
+               {.temperature = 115, .backoff = 3000},
+               {.temperature = 116, .backoff = 5000},
+               {.temperature = 117, .backoff = 10000},
+       },
+       .support_ct_kill = true,
+       .support_dynamic_smps = true,
+       .support_tx_protection = true,
+       .support_tx_backoff = true,
+};
+
 void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
 {
        struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
 
        IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
-       tt->params = &iwl7000_tt_params;
+
+       if (mvm->cfg->high_temp)
+               tt->params = &iwl7000_high_temp_tt_params;
+       else
+               tt->params = &iwl7000_tt_params;
+
        tt->throttle = false;
        INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
 }
index f0e96a9..e05440d 100644 (file)
@@ -91,11 +91,10 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
 
        /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
-       if (info->band == IEEE80211_BAND_2GHZ        &&
-           (skb->protocol == cpu_to_be16(ETH_P_PAE)  ||
-            is_multicast_ether_addr(hdr->addr1)      ||
-            ieee80211_is_back_req(fc)                ||
-            ieee80211_is_mgmt(fc)))
+       if (info->band == IEEE80211_BAND_2GHZ &&
+           (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ||
+            is_multicast_ether_addr(hdr->addr1) ||
+            ieee80211_is_back_req(fc) || ieee80211_is_mgmt(fc)))
                tx_flags |= TX_CMD_FLG_BT_DIS;
 
        if (ieee80211_has_morefrags(fc))
@@ -123,6 +122,8 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
                 * it
                 */
                WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+       } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+               tx_cmd->pm_frame_timeout = cpu_to_le16(2);
        } else {
                tx_cmd->pm_frame_timeout = 0;
        }
@@ -171,7 +172,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
        }
 
        /*
-        * for data packets, rate info comes from the table inside he fw. This
+        * for data packets, rate info comes from the table inside the fw. This
         * table is controlled by LINK_QUALITY commands
         */
 
index 1e13328..a9c3574 100644 (file)
@@ -453,6 +453,29 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
        IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
 }
 
+void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
+{
+       const struct fw_img *img;
+       int ofs, len = 0;
+       u8 *buf;
+
+       if (!mvm->ucode_loaded)
+               return;
+
+       img = &mvm->fw->img[mvm->cur_ucode];
+       ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       buf = kzalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
+       iwl_print_hex_error(mvm->trans, buf, len);
+
+       kfree(buf);
+}
+
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
index ff13458..dc02cb9 100644 (file)
@@ -273,9 +273,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
+       {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
@@ -325,15 +325,15 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        int ret;
 
        iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg);
-       if (iwl_trans == NULL)
-               return -ENOMEM;
+       if (IS_ERR(iwl_trans))
+               return PTR_ERR(iwl_trans);
 
        pci_set_drvdata(pdev, iwl_trans);
 
        trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
        trans_pcie->drv = iwl_drv_start(iwl_trans, cfg);
 
-       if (IS_ERR_OR_NULL(trans_pcie->drv)) {
+       if (IS_ERR(trans_pcie->drv)) {
                ret = PTR_ERR(trans_pcie->drv);
                goto out_free_trans;
        }
@@ -368,21 +368,19 @@ static void iwl_pci_remove(struct pci_dev *pdev)
 
 static int iwl_pci_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct iwl_trans *iwl_trans = pci_get_drvdata(pdev);
-
        /* Before you put code here, think about WoWLAN. You cannot check here
         * whether WoWLAN is enabled or not, and your code will run even if
         * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
         */
 
-       return iwl_trans_suspend(iwl_trans);
+       return 0;
 }
 
 static int iwl_pci_resume(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
-       struct iwl_trans *iwl_trans = pci_get_drvdata(pdev);
+       struct iwl_trans *trans = pci_get_drvdata(pdev);
+       bool hw_rfkill;
 
        /* Before you put code here, think about WoWLAN. You cannot check here
         * whether WoWLAN is enabled or not, and your code will run even if
@@ -395,7 +393,15 @@ static int iwl_pci_resume(struct device *device)
         */
        pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
 
-       return iwl_trans_resume(iwl_trans);
+       if (!trans->op_mode)
+               return 0;
+
+       iwl_enable_rfkill_int(trans);
+
+       hw_rfkill = iwl_is_rfkill_set(trans);
+       iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+
+       return 0;
 }
 
 static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
index b654dcd..fa22639 100644 (file)
@@ -392,7 +392,6 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 /*****************************************************
 * Error handling
 ******************************************************/
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf);
 void iwl_pcie_dump_csr(struct iwl_trans *trans);
 
 /*****************************************************
index fd848cd..3f237b4 100644 (file)
  */
 static int iwl_rxq_space(const struct iwl_rxq *rxq)
 {
-       int s = rxq->read - rxq->write;
-
-       if (s <= 0)
-               s += RX_QUEUE_SIZE;
-       /* keep some buffer to not confuse full and empty queue */
-       s -= 2;
-       if (s < 0)
-               s = 0;
-       return s;
+       /* Make sure RX_QUEUE_SIZE is a power of 2 */
+       BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1));
+
+       /*
+        * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity
+        * between empty and completely full queues.
+        * The following is equivalent to modulo by RX_QUEUE_SIZE and is well
+        * defined for negative dividends.
+        */
+       return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1);
 }
 
 /*
@@ -793,7 +794,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
        }
 
        iwl_pcie_dump_csr(trans);
-       iwl_pcie_dump_fh(trans, NULL);
+       iwl_dump_fh(trans, NULL);
 
        set_bit(STATUS_FW_ERROR, &trans_pcie->status);
        clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
@@ -1120,6 +1121,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
        struct iwl_trans *trans = data;
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 inta, inta_mask;
+       irqreturn_t ret = IRQ_NONE;
 
        lockdep_assert_held(&trans_pcie->irq_lock);
 
@@ -1168,10 +1170,8 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
        /* the thread will service interrupts and re-enable them */
        if (likely(inta))
                return IRQ_WAKE_THREAD;
-       else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
-                !trans_pcie->inta)
-               iwl_enable_interrupts(trans);
-       return IRQ_HANDLED;
+
+       ret = IRQ_HANDLED;
 
 none:
        /* re-enable interrupts here since we don't have anything to service. */
@@ -1180,7 +1180,7 @@ none:
            !trans_pcie->inta)
                iwl_enable_interrupts(trans);
 
-       return IRQ_NONE;
+       return ret;
 }
 
 /* interrupt handler using ict table, with this interrupt driver will
@@ -1199,6 +1199,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
        u32 val = 0;
        u32 read;
        unsigned long flags;
+       irqreturn_t ret = IRQ_NONE;
 
        if (!trans)
                return IRQ_NONE;
@@ -1211,7 +1212,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
         * use legacy interrupt.
         */
        if (unlikely(!trans_pcie->use_ict)) {
-               irqreturn_t ret = iwl_pcie_isr(irq, data);
+               ret = iwl_pcie_isr(irq, data);
                spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
                return ret;
        }
@@ -1280,17 +1281,9 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
        if (likely(inta)) {
                spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
                return IRQ_WAKE_THREAD;
-       } else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
-                !trans_pcie->inta) {
-               /* Allow interrupt if was disabled by this handler and
-                * no tasklet was schedules, We should not enable interrupt,
-                * tasklet will enable it.
-                */
-               iwl_enable_interrupts(trans);
        }
 
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-       return IRQ_HANDLED;
+       ret = IRQ_HANDLED;
 
  none:
        /* re-enable interrupts here since we don't have anything to service.
@@ -1301,5 +1294,5 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
                iwl_enable_interrupts(trans);
 
        spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-       return IRQ_NONE;
+       return ret;
 }
index 390e2f0..bad95d2 100644 (file)
@@ -820,25 +820,6 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
                clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
-{
-       return 0;
-}
-
-static int iwl_trans_pcie_resume(struct iwl_trans *trans)
-{
-       bool hw_rfkill;
-
-       iwl_enable_rfkill_int(trans);
-
-       hw_rfkill = iwl_is_rfkill_set(trans);
-       iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
-
-       return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
 static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
                                                unsigned long *flags)
 {
@@ -1038,71 +1019,6 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 }
 
-static const char *get_fh_string(int cmd)
-{
-#define IWL_CMD(x) case x: return #x
-       switch (cmd) {
-       IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
-       IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
-       IWL_CMD(FH_RSCSR_CHNL0_WPTR);
-       IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
-       IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
-       IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
-       IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
-       IWL_CMD(FH_TSSR_TX_STATUS_REG);
-       IWL_CMD(FH_TSSR_TX_ERROR_REG);
-       default:
-               return "UNKNOWN";
-       }
-#undef IWL_CMD
-}
-
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf)
-{
-       int i;
-       static const u32 fh_tbl[] = {
-               FH_RSCSR_CHNL0_STTS_WPTR_REG,
-               FH_RSCSR_CHNL0_RBDCB_BASE_REG,
-               FH_RSCSR_CHNL0_WPTR,
-               FH_MEM_RCSR_CHNL0_CONFIG_REG,
-               FH_MEM_RSSR_SHARED_CTRL_REG,
-               FH_MEM_RSSR_RX_STATUS_REG,
-               FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
-               FH_TSSR_TX_STATUS_REG,
-               FH_TSSR_TX_ERROR_REG
-       };
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (buf) {
-               int pos = 0;
-               size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
-
-               *buf = kmalloc(bufsz, GFP_KERNEL);
-               if (!*buf)
-                       return -ENOMEM;
-
-               pos += scnprintf(*buf + pos, bufsz - pos,
-                               "FH register values:\n");
-
-               for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
-                       pos += scnprintf(*buf + pos, bufsz - pos,
-                               "  %34s: 0X%08x\n",
-                               get_fh_string(fh_tbl[i]),
-                               iwl_read_direct32(trans, fh_tbl[i]));
-
-               return pos;
-       }
-#endif
-
-       IWL_ERR(trans, "FH register values:\n");
-       for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
-               IWL_ERR(trans, "  %34s: 0X%08x\n",
-                       get_fh_string(fh_tbl[i]),
-                       iwl_read_direct32(trans, fh_tbl[i]));
-
-       return 0;
-}
-
 static const char *get_csr_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -1183,18 +1099,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans)
 } while (0)
 
 /* file operation */
-#define DEBUGFS_READ_FUNC(name)                                         \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file,               \
-                                       char __user *user_buf,          \
-                                       size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name)                                        \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file,              \
-                                       const char __user *user_buf,    \
-                                       size_t count, loff_t *ppos);
-
 #define DEBUGFS_READ_FILE_OPS(name)                                    \
-       DEBUGFS_READ_FUNC(name);                                        \
 static const struct file_operations iwl_dbgfs_##name##_ops = {         \
        .read = iwl_dbgfs_##name##_read,                                \
        .open = simple_open,                                            \
@@ -1202,7 +1107,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {            \
 };
 
 #define DEBUGFS_WRITE_FILE_OPS(name)                                    \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .open = simple_open,                                            \
@@ -1210,8 +1114,6 @@ static const struct file_operations iwl_dbgfs_##name##_ops = {          \
 };
 
 #define DEBUGFS_READ_WRITE_FILE_OPS(name)                              \
-       DEBUGFS_READ_FUNC(name);                                        \
-       DEBUGFS_WRITE_FUNC(name);                                       \
 static const struct file_operations iwl_dbgfs_##name##_ops = {         \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
@@ -1395,7 +1297,7 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
        int pos = 0;
        ssize_t ret = -EFAULT;
 
-       ret = pos = iwl_pcie_dump_fh(trans, &buf);
+       ret = pos = iwl_dump_fh(trans, &buf);
        if (buf) {
                ret = simple_read_from_buffer(user_buf,
                                              count, ppos, buf, pos);
@@ -1459,10 +1361,6 @@ static const struct iwl_trans_ops trans_ops_pcie = {
 
        .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
 
-#ifdef CONFIG_PM_SLEEP
-       .suspend = iwl_trans_pcie_suspend,
-       .resume = iwl_trans_pcie_resume,
-#endif
        .write8 = iwl_trans_pcie_write8,
        .write32 = iwl_trans_pcie_write32,
        .read32 = iwl_trans_pcie_read32,
@@ -1488,9 +1386,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 
        trans = kzalloc(sizeof(struct iwl_trans) +
                        sizeof(struct iwl_trans_pcie), GFP_KERNEL);
-
-       if (!trans)
-               return NULL;
+       if (!trans) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
@@ -1502,15 +1401,20 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
-       if (pci_enable_device(pdev)) {
-               err = -ENODEV;
-               goto out_no_pci;
+       if (!cfg->base_params->pcie_l1_allowed) {
+               /*
+                * W/A - seems to solve weird behavior. We need to remove this
+                * if we don't want to stay in L1 all the time. This wastes a
+                * lot of power.
+                */
+               pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+                                      PCIE_LINK_STATE_L1 |
+                                      PCIE_LINK_STATE_CLKPM);
        }
 
-       /* W/A - seems to solve weird behavior. We need to remove this if we
-        * don't want to stay in L1 all the time. This wastes a lot of power */
-       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
-                              PCIE_LINK_STATE_CLKPM);
+       err = pci_enable_device(pdev);
+       if (err)
+               goto out_no_pci;
 
        pci_set_master(pdev);
 
@@ -1579,17 +1483,20 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                                  SLAB_HWCACHE_ALIGN,
                                  NULL);
 
-       if (!trans->dev_cmd_pool)
+       if (!trans->dev_cmd_pool) {
+               err = -ENOMEM;
                goto out_pci_disable_msi;
+       }
 
        trans_pcie->inta_mask = CSR_INI_SET_MASK;
 
        if (iwl_pcie_alloc_ict(trans))
                goto out_free_cmd_pool;
 
-       if (request_threaded_irq(pdev->irq, iwl_pcie_isr_ict,
-                                iwl_pcie_irq_handler,
-                                IRQF_SHARED, DRV_NAME, trans)) {
+       err = request_threaded_irq(pdev->irq, iwl_pcie_isr_ict,
+                                  iwl_pcie_irq_handler,
+                                  IRQF_SHARED, DRV_NAME, trans);
+       if (err) {
                IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
                goto out_free_ict;
        }
@@ -1608,5 +1515,6 @@ out_pci_disable_device:
        pci_disable_device(pdev);
 out_no_pci:
        kfree(trans);
-       return NULL;
+out:
+       return ERR_PTR(err);
 }
index c47c921..f45eb29 100644 (file)
  ***************************************************/
 static int iwl_queue_space(const struct iwl_queue *q)
 {
-       int s = q->read_ptr - q->write_ptr;
-
-       if (q->read_ptr > q->write_ptr)
-               s -= q->n_bd;
-
-       if (s <= 0)
-               s += q->n_window;
-       /* keep some reserve to not confuse empty and full situations */
-       s -= 2;
-       if (s < 0)
-               s = 0;
-       return s;
+       unsigned int max;
+       unsigned int used;
+
+       /*
+        * To avoid ambiguity between empty and completely full queues, there
+        * should always be less than q->n_bd elements in the queue.
+        * If q->n_window is smaller than q->n_bd, there is no need to reserve
+        * any queue entries for this purpose.
+        */
+       if (q->n_window < q->n_bd)
+               max = q->n_window;
+       else
+               max = q->n_bd - 1;
+
+       /*
+        * q->n_bd is a power of 2, so the following is equivalent to modulo by
+        * q->n_bd and is well defined for negative dividends.
+        */
+       used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
+
+       if (WARN_ON(used > max))
+               return 0;
+
+       return max - used;
 }
 
 /*
@@ -451,13 +463,10 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
                return -EINVAL;
        }
 
-       if (WARN_ON(addr & ~DMA_BIT_MASK(36)))
+       if (WARN(addr & ~IWL_TX_DMA_MASK,
+                "Unaligned address = %llx\n", (unsigned long long)addr))
                return -EINVAL;
 
-       if (unlikely(addr & ~IWL_TX_DMA_MASK))
-               IWL_ERR(trans, "Unaligned address = %llx\n",
-                       (unsigned long long)addr);
-
        iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len);
 
        return 0;
@@ -829,7 +838,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans)
                                  sizeof(struct iwl_txq), GFP_KERNEL);
        if (!trans_pcie->txq) {
                IWL_ERR(trans, "Not enough memory for txq\n");
-               ret = ENOMEM;
+               ret = -ENOMEM;
                goto error;
        }
 
@@ -1153,10 +1162,10 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
 /*
  * iwl_pcie_enqueue_hcmd - enqueue a uCode command
  * @priv: device private data point
- * @cmd: a point to the ucode command structure
+ * @cmd: a pointer to the ucode command structure
  *
- * The function returns < 0 values to indicate the operation is
- * failed. On success, it turns the index (> 0) of command in the
+ * The function returns < 0 values to indicate the operation
+ * failed. On success, it returns the index (>= 0) of command in the
  * command queue.
  */
 static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
@@ -1619,10 +1628,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
 
-       if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
-               WARN_ON_ONCE(1);
+       if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used),
+                     "TX on unused queue %d\n", txq_id))
                return -EINVAL;
-       }
 
        spin_lock(&txq->lock);
 
@@ -1632,7 +1640,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         * Check here that the packets are in the right place on the ring.
         */
        wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
-       WARN_ONCE(trans_pcie->txq[txq_id].ampdu &&
+       WARN_ONCE(txq->ampdu &&
                  (wifi_seq & 0xff) != q->write_ptr,
                  "Q: %d WiFi Seq %d tfdNum %d",
                  txq_id, wifi_seq, q->write_ptr);
@@ -1664,7 +1672,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         */
        len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
              hdr_len - IWL_HCMD_SCRATCHBUF_SIZE;
-       tb1_len = (len + 3) & ~3;
+       tb1_len = ALIGN(len, 4);
 
        /* Tell NIC about any 2-byte padding after MAC header */
        if (tb1_len != len)
index cb34c78..a0d2aac 100644 (file)
@@ -867,7 +867,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
 
        if (WARN_ON(skb->len < 10)) {
                /* Should not happen; just a sanity check for addr1 use */
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -884,13 +884,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        }
 
        if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
        if (data->idle && !data->tmp_chan) {
                wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
-               dev_kfree_skb(skb);
+               ieee80211_free_txskb(hw, skb);
                return;
        }
 
@@ -1364,6 +1364,7 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = {
 static int hwsim_fops_ps_write(void *dat, u64 val);
 
 static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
                                       void *data, int len)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
@@ -2309,7 +2310,9 @@ static int __init init_mac80211_hwsim(void)
                        hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
 
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
-                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                                   WIPHY_FLAG_AP_UAPSD;
+               hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
                /* ask mac80211 to reserve space for magic */
                hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index 41e9d25..0b803c0 100644 (file)
@@ -292,6 +292,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
        struct mwifiex_ie_types_extcap *ext_cap;
        int ret_len = 0;
        struct ieee80211_supported_band *sband;
+       struct ieee_types_header *hdr;
        u8 radio_type;
 
        if (!buffer || !*buffer)
@@ -388,17 +389,24 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
        }
 
        if (bss_desc->bcn_ext_cap) {
+               hdr = (void *)bss_desc->bcn_ext_cap;
                ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;
                memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));
                ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
-               ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap));
+               ext_cap->header.len = cpu_to_le16(hdr->len);
 
-               memcpy((u8 *)ext_cap + sizeof(struct mwifiex_ie_types_header),
+               memcpy((u8 *)ext_cap->ext_capab,
                       bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
                       le16_to_cpu(ext_cap->header.len));
 
-               *buffer += sizeof(struct mwifiex_ie_types_extcap);
-               ret_len += sizeof(struct mwifiex_ie_types_extcap);
+               if (hdr->len > 3 &&
+                   ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
+                       priv->hs2_enabled = true;
+               else
+                       priv->hs2_enabled = false;
+
+               *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
+               ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
        }
 
        return ret_len;
index a78e065..f69d7e0 100644 (file)
@@ -69,7 +69,7 @@ mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
        memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
 
        /* Copy SNAP header */
-       snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset);
+       snap.snap_type = le16_to_cpu(*(__le16 *) ((u8 *)skb_src->data + dt_offset));
        dt_offset += sizeof(u16);
 
        memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
@@ -189,7 +189,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
                skb_src = skb_dequeue(&pra_list->skb_head);
 
-               pra_list->total_pkts_size -= skb_src->len;
+               pra_list->total_pkt_count--;
 
                atomic_dec(&priv->wmm.tx_pkts_queued);
 
@@ -268,7 +268,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
                skb_queue_tail(&pra_list->skb_head, skb_aggr);
 
-               pra_list->total_pkts_size += skb_aggr->len;
+               pra_list->total_pkt_count++;
 
                atomic_inc(&priv->wmm.tx_pkts_queued);
 
index 89459db..fbad00a 100644 (file)
@@ -25,7 +25,9 @@ module_param(reg_alpha2, charp, 0);
 
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
-               .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
+               .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
+                                  BIT(NL80211_IFTYPE_P2P_GO) |
+                                  BIT(NL80211_IFTYPE_P2P_CLIENT),
        },
        {
                .max = 1, .types = BIT(NL80211_IFTYPE_AP),
@@ -189,6 +191,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct sk_buff *skb;
        u16 pkt_len;
        const struct ieee80211_mgmt *mgmt;
+       struct mwifiex_txinfo *tx_info;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
 
        if (!buf || !len) {
@@ -216,6 +219,10 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -ENOMEM;
        }
 
+       tx_info = MWIFIEX_SKB_TXCB(skb);
+       tx_info->bss_num = priv->bss_num;
+       tx_info->bss_type = priv->bss_type;
+
        mwifiex_form_mgmt_frame(skb, buf, len);
        mwifiex_queue_tx_pkt(priv, skb);
 
@@ -235,16 +242,20 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
                                     u16 frame_type, bool reg)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+       u32 mask;
 
        if (reg)
-               priv->mgmt_frame_mask |= BIT(frame_type >> 4);
+               mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
        else
-               priv->mgmt_frame_mask &= ~BIT(frame_type >> 4);
-
-       mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
-                              HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask);
+               mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
 
-       wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       if (mask != priv->mgmt_frame_mask) {
+               priv->mgmt_frame_mask = mask;
+               mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
+                                      HostCmd_ACT_GEN_SET, 0,
+                                      &priv->mgmt_frame_mask);
+               wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+       }
 }
 
 /*
@@ -1497,6 +1508,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
                " reason code %d\n", priv->cfg_bssid, reason_code);
 
        memset(priv->cfg_bssid, 0, ETH_ALEN);
+       priv->hs2_enabled = false;
 
        return 0;
 }
@@ -2296,10 +2308,9 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 }
 EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
 
-#ifdef CONFIG_PM
 static bool
-mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
-                            s8 *byte_seq)
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
+                            u8 max_byte_seq)
 {
        int j, k, valid_byte_cnt = 0;
        bool dont_care_byte = false;
@@ -2317,16 +2328,17 @@ mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
                                        dont_care_byte = true;
                        }
 
-                       if (valid_byte_cnt > MAX_BYTESEQ)
+                       if (valid_byte_cnt > max_byte_seq)
                                return false;
                }
        }
 
-       byte_seq[MAX_BYTESEQ] = valid_byte_cnt;
+       byte_seq[max_byte_seq] = valid_byte_cnt;
 
        return true;
 }
 
+#ifdef CONFIG_PM
 static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
                                    struct cfg80211_wowlan *wowlan)
 {
@@ -2335,7 +2347,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
        struct mwifiex_mef_entry *mef_entry;
        int i, filt_num = 0, ret;
        bool first_pat = true;
-       u8 byte_seq[MAX_BYTESEQ + 1];
+       u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
        const u8 ipv4_mc_mac[] = {0x33, 0x33};
        const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
        struct mwifiex_private *priv =
@@ -2365,7 +2377,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
        for (i = 0; i < wowlan->n_patterns; i++) {
                memset(byte_seq, 0, sizeof(byte_seq));
                if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
-                                                 byte_seq)) {
+                                                 byte_seq,
+                                                 MWIFIEX_MEF_MAX_BYTESEQ)) {
                        wiphy_err(wiphy, "Pattern not supported\n");
                        kfree(mef_entry);
                        return -EOPNOTSUPP;
@@ -2373,16 +2386,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
 
                if (!wowlan->patterns[i].pkt_offset) {
                        if (!(byte_seq[0] & 0x01) &&
-                           (byte_seq[MAX_BYTESEQ] == 1)) {
+                           (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
                                mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
                                continue;
                        } else if (is_broadcast_ether_addr(byte_seq)) {
                                mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
                                continue;
                        } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
-                                   (byte_seq[MAX_BYTESEQ] == 2)) ||
+                                   (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
                                   (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
-                                   (byte_seq[MAX_BYTESEQ] == 3))) {
+                                   (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
                                mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
                                continue;
                        }
@@ -2408,7 +2421,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
                mef_entry->filter[filt_num].repeat = 16;
                memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
                       ETH_ALEN);
-               mef_entry->filter[filt_num].byte_seq[MAX_BYTESEQ] = ETH_ALEN;
+               mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+                                                               ETH_ALEN;
                mef_entry->filter[filt_num].offset = 14;
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                if (filt_num)
@@ -2442,6 +2456,119 @@ static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy,
 }
 #endif
 
+static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
+{
+       const u8 ipv4_mc_mac[] = {0x33, 0x33};
+       const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+       const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
+
+       if ((byte_seq[0] & 0x01) &&
+           (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
+               return PACKET_TYPE_UNICAST;
+       else if (!memcmp(byte_seq, bc_mac, 4))
+               return PACKET_TYPE_BROADCAST;
+       else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+                 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
+                (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+                 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
+               return PACKET_TYPE_MULTICAST;
+
+       return 0;
+}
+
+static int
+mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
+                               struct cfg80211_coalesce_rules *crule,
+                               struct mwifiex_coalesce_rule *mrule)
+{
+       u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
+       struct filt_field_param *param;
+       int i;
+
+       mrule->max_coalescing_delay = crule->delay;
+
+       param = mrule->params;
+
+       for (i = 0; i < crule->n_patterns; i++) {
+               memset(byte_seq, 0, sizeof(byte_seq));
+               if (!mwifiex_is_pattern_supported(&crule->patterns[i],
+                                                 byte_seq,
+                                               MWIFIEX_COALESCE_MAX_BYTESEQ)) {
+                       dev_err(priv->adapter->dev, "Pattern not supported\n");
+                       return -EOPNOTSUPP;
+               }
+
+               if (!crule->patterns[i].pkt_offset) {
+                       u8 pkt_type;
+
+                       pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
+                       if (pkt_type && mrule->pkt_type) {
+                               dev_err(priv->adapter->dev,
+                                       "Multiple packet types not allowed\n");
+                               return -EOPNOTSUPP;
+                       } else if (pkt_type) {
+                               mrule->pkt_type = pkt_type;
+                               continue;
+                       }
+               }
+
+               if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
+                       param->operation = RECV_FILTER_MATCH_TYPE_EQ;
+               else
+                       param->operation = RECV_FILTER_MATCH_TYPE_NE;
+
+               param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
+               memcpy(param->operand_byte_stream, byte_seq,
+                      param->operand_len);
+               param->offset = crule->patterns[i].pkt_offset;
+               param++;
+
+               mrule->num_of_fields++;
+       }
+
+       if (!mrule->pkt_type) {
+               dev_err(priv->adapter->dev,
+                       "Packet type can not be determined\n");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
+                                        struct cfg80211_coalesce *coalesce)
+{
+       struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+       int i, ret;
+       struct mwifiex_ds_coalesce_cfg coalesce_cfg;
+       struct mwifiex_private *priv =
+                       mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+       memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
+       if (!coalesce) {
+               dev_dbg(adapter->dev,
+                       "Disable coalesce and reset all previous rules\n");
+               return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
+                                            HostCmd_ACT_GEN_SET, 0,
+                                            &coalesce_cfg);
+       }
+
+       coalesce_cfg.num_of_rules = coalesce->n_rules;
+       for (i = 0; i < coalesce->n_rules; i++) {
+               ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
+                                                     &coalesce_cfg.rule[i]);
+               if (ret) {
+                       dev_err(priv->adapter->dev,
+                               "Recheck the patterns provided for rule %d\n",
+                               i + 1);
+                       return ret;
+               }
+       }
+
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
+                                    HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
+}
+
 /* station cfg80211 operations */
 static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .add_virtual_intf = mwifiex_add_virtual_intf,
@@ -2476,12 +2603,13 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .resume = mwifiex_cfg80211_resume,
        .set_wakeup = mwifiex_cfg80211_set_wakeup,
 #endif
+       .set_coalesce = mwifiex_cfg80211_set_coalesce,
 };
 
 #ifdef CONFIG_PM
 static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
        .flags = WIPHY_WOWLAN_MAGIC_PKT,
-       .n_patterns = MWIFIEX_MAX_FILTERS,
+       .n_patterns = MWIFIEX_MEF_MAX_FILTERS,
        .pattern_min_len = 1,
        .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
        .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
@@ -2499,6 +2627,15 @@ static bool mwifiex_is_valid_alpha2(const char *alpha2)
        return false;
 }
 
+static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
+       .n_rules = MWIFIEX_COALESCE_MAX_RULES,
+       .max_delay = MWIFIEX_MAX_COALESCING_DELAY,
+       .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
@@ -2560,6 +2697,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
        wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
+       wiphy->coalesce = &mwifiex_coalesce_support;
+
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
index 5178c46..9eefacb 100644 (file)
@@ -404,11 +404,43 @@ mwifiex_is_rate_auto(struct mwifiex_private *priv)
                return false;
 }
 
-/*
- * This function gets the supported data rates.
- *
- * The function works in both Ad-Hoc and infra mode by printing the
- * band and returning the data rates.
+/* This function gets the supported data rates from bitmask inside
+ * cfg80211_scan_request.
+ */
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+                                   u8 *rates, u8 radio_type)
+{
+       struct wiphy *wiphy = priv->adapter->wiphy;
+       struct cfg80211_scan_request *request = priv->scan_request;
+       u32 num_rates, rate_mask;
+       struct ieee80211_supported_band *sband;
+       int i;
+
+       if (radio_type) {
+               sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+               if (WARN_ON_ONCE(!sband))
+                       return 0;
+               rate_mask = request->rates[IEEE80211_BAND_5GHZ];
+       } else {
+               sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+               if (WARN_ON_ONCE(!sband))
+                       return 0;
+               rate_mask = request->rates[IEEE80211_BAND_2GHZ];
+       }
+
+       num_rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((BIT(i) & rate_mask) == 0)
+                       continue; /* skip rate */
+               rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
+       }
+
+       return num_rates;
+}
+
+/* This function gets the supported data rates. The function works in
+ * both Ad-Hoc and infra mode by printing the band and returning the
+ * data rates.
  */
 u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
 {
index 94cc09d..5c85d78 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/wait.h>
 #include <linux/timer.h>
 #include <linux/ieee80211.h>
+#include <uapi/linux/if_arp.h>
 #include <net/mac80211.h>
 
 
@@ -75,7 +76,8 @@
 #define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
 #define MWIFIEX_BUF_FLAG_BRIDGED_PKT      BIT(1)
 
-#define MWIFIEX_BRIDGED_PKTS_THRESHOLD     1024
+#define MWIFIEX_BRIDGED_PKTS_THR_HIGH      1024
+#define MWIFIEX_BRIDGED_PKTS_THR_LOW        128
 
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
@@ -151,4 +153,12 @@ struct mwifiex_types_wmm_info {
        u8 reserved;
        struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
 } __packed;
+
+struct mwifiex_arp_eth_header {
+       struct arphdr hdr;
+       u8 ar_sha[ETH_ALEN];
+       u8 ar_sip[4];
+       u8 ar_tha[ETH_ALEN];
+       u8 ar_tip[4];
+} __packed;
 #endif /* !_MWIFIEX_DECL_H_ */
index 1b45aa5..f80f30b 100644 (file)
@@ -85,9 +85,6 @@ enum KEY_TYPE_ID {
 #define WAPI_KEY_LEN                   50
 
 #define MAX_POLL_TRIES                 100
-
-#define MAX_MULTI_INTERFACE_POLL_TRIES  1000
-
 #define MAX_FIRMWARE_POLL_TRIES                        100
 
 #define FIRMWARE_READY_SDIO                            0xfedc
@@ -156,6 +153,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_UAP_PS_AO_TIMER    (PROPRIETARY_TLV_BASE_ID + 123)
 #define TLV_TYPE_PWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 145)
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 
 #define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
 
@@ -297,6 +295,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_CAU_REG_ACCESS                    0x00ed
 #define HostCmd_CMD_SET_BSS_MODE                      0x00f7
 #define HostCmd_CMD_PCIE_DESC_DETAILS                 0x00fa
+#define HostCmd_CMD_COALESCE_CFG                      0x010a
 #define HostCmd_CMD_MGMT_FRAME_REG                    0x010c
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
 #define HostCmd_CMD_11AC_CFG                         0x0112
@@ -453,7 +452,7 @@ enum P2P_MODES {
        (((event_cause) >> 24) & 0x00ff)
 
 #define MWIFIEX_MAX_PATTERN_LEN                20
-#define MWIFIEX_MAX_OFFSET_LEN         50
+#define MWIFIEX_MAX_OFFSET_LEN         100
 #define STACK_NBYTES                   100
 #define TYPE_DNUM                      1
 #define TYPE_BYTESEQ                   2
@@ -1331,7 +1330,7 @@ struct mwifiex_ie_types_2040bssco {
 
 struct mwifiex_ie_types_extcap {
        struct mwifiex_ie_types_header header;
-       u8 ext_cap;
+       u8 ext_capab[0];
 } __packed;
 
 struct host_cmd_ds_mac_reg_access {
@@ -1369,11 +1368,6 @@ struct host_cmd_ds_802_11_eeprom_access {
        u8 value;
 } __packed;
 
-struct host_cmd_tlv {
-       __le16 type;
-       __le16 len;
-} __packed;
-
 struct mwifiex_assoc_event {
        u8 sta_addr[ETH_ALEN];
        __le16 type;
@@ -1399,99 +1393,99 @@ struct host_cmd_11ac_vht_cfg {
 } __packed;
 
 struct host_cmd_tlv_akmp {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 key_mgmt;
        __le16 key_mgmt_operation;
 } __packed;
 
 struct host_cmd_tlv_pwk_cipher {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 proto;
        u8 cipher;
        u8 reserved;
 } __packed;
 
 struct host_cmd_tlv_gwk_cipher {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 cipher;
        u8 reserved;
 } __packed;
 
 struct host_cmd_tlv_passphrase {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 passphrase[0];
 } __packed;
 
 struct host_cmd_tlv_wep_key {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 key_index;
        u8 is_default;
        u8 key[1];
 };
 
 struct host_cmd_tlv_auth_type {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 auth_type;
 } __packed;
 
 struct host_cmd_tlv_encrypt_protocol {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 proto;
 } __packed;
 
 struct host_cmd_tlv_ssid {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 ssid[0];
 } __packed;
 
 struct host_cmd_tlv_rates {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 rates[0];
 } __packed;
 
 struct host_cmd_tlv_bcast_ssid {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 bcast_ctl;
 } __packed;
 
 struct host_cmd_tlv_beacon_period {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 period;
 } __packed;
 
 struct host_cmd_tlv_dtim_period {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 period;
 } __packed;
 
 struct host_cmd_tlv_frag_threshold {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 frag_thr;
 } __packed;
 
 struct host_cmd_tlv_rts_threshold {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le16 rts_thr;
 } __packed;
 
 struct host_cmd_tlv_retry_limit {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 limit;
 } __packed;
 
 struct host_cmd_tlv_mac_addr {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 mac_addr[ETH_ALEN];
 } __packed;
 
 struct host_cmd_tlv_channel_band {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        u8 band_config;
        u8 channel;
 } __packed;
 
 struct host_cmd_tlv_ageout_timer {
-       struct host_cmd_tlv tlv;
+       struct mwifiex_ie_types_header header;
        __le32 sta_ao_timer;
 } __packed;
 
@@ -1604,6 +1598,27 @@ struct host_cmd_ds_802_11_cfg_data {
        __le16 data_len;
 } __packed;
 
+struct coalesce_filt_field_param {
+       u8 operation;
+       u8 operand_len;
+       __le16 offset;
+       u8 operand_byte_stream[4];
+};
+
+struct coalesce_receive_filt_rule {
+       struct mwifiex_ie_types_header header;
+       u8 num_of_fields;
+       u8 pkt_type;
+       __le16 max_coalescing_delay;
+       struct coalesce_filt_field_param params[0];
+} __packed;
+
+struct host_cmd_ds_coalesce_cfg {
+       __le16 action;
+       __le16 num_of_rules;
+       struct coalesce_receive_filt_rule rule[0];
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
@@ -1664,6 +1679,7 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_sta_deauth sta_deauth;
                struct host_cmd_11ac_vht_cfg vht_cfg;
                struct host_cmd_ds_802_11_cfg_data cfg_data;
+               struct host_cmd_ds_coalesce_cfg coalesce_cfg;
        } params;
 } __packed;
 
index e38342f..220af4f 100644 (file)
@@ -87,7 +87,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
        u8 *tmp;
 
        input_len = le16_to_cpu(ie_list->len);
-       travel_len = sizeof(struct host_cmd_tlv);
+       travel_len = sizeof(struct mwifiex_ie_types_header);
 
        ie_list->len = 0;
 
index 2cf8b96..6499117 100644 (file)
@@ -135,6 +135,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
 
        priv->csa_chan = 0;
        priv->csa_expire_time = 0;
+       priv->del_list_idx = 0;
+       priv->hs2_enabled = false;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
@@ -377,18 +379,11 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
 static void
 mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
 {
-       int i;
-
        if (!adapter) {
                pr_err("%s: adapter is NULL\n", __func__);
                return;
        }
 
-       for (i = 0; i < adapter->priv_num; i++) {
-               if (adapter->priv[i])
-                       del_timer_sync(&adapter->priv[i]->scan_delay_timer);
-       }
-
        mwifiex_cancel_all_pending_cmd(adapter);
 
        /* Free lock variables */
@@ -398,13 +393,8 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
        dev_dbg(adapter->dev, "info: free cmd buffer\n");
        mwifiex_free_cmd_buffer(adapter);
 
-       del_timer(&adapter->cmd_timer);
-
        dev_dbg(adapter->dev, "info: free scan table\n");
 
-       if (adapter->if_ops.cleanup_if)
-               adapter->if_ops.cleanup_if(adapter);
-
        if (adapter->sleep_cfm)
                dev_kfree_skb_any(adapter->sleep_cfm);
 }
@@ -702,7 +692,6 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
                if (!adapter->winner) {
                        dev_notice(adapter->dev,
                                   "FW already running! Skip FW dnld\n");
-                       poll_num = MAX_MULTI_INTERFACE_POLL_TRIES;
                        goto poll_fw;
                }
        }
index 7f27e45..00a95f4 100644 (file)
@@ -362,13 +362,13 @@ struct mwifiex_ds_misc_subsc_evt {
        struct subsc_evt_cfg bcn_h_rssi_cfg;
 };
 
-#define MAX_BYTESEQ            6       /* non-adjustable */
-#define MWIFIEX_MAX_FILTERS    10
+#define MWIFIEX_MEF_MAX_BYTESEQ                6       /* non-adjustable */
+#define MWIFIEX_MEF_MAX_FILTERS                10
 
 struct mwifiex_mef_filter {
        u16 repeat;
        u16 offset;
-       s8 byte_seq[MAX_BYTESEQ + 1];
+       s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
        u8 filt_type;
        u8 filt_action;
 };
@@ -376,7 +376,7 @@ struct mwifiex_mef_filter {
 struct mwifiex_mef_entry {
        u8 mode;
        u8 action;
-       struct mwifiex_mef_filter filter[MWIFIEX_MAX_FILTERS];
+       struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS];
 };
 
 struct mwifiex_ds_mef_cfg {
@@ -397,4 +397,39 @@ enum {
        MWIFIEX_FUNC_SHUTDOWN,
 };
 
+enum COALESCE_OPERATION {
+       RECV_FILTER_MATCH_TYPE_EQ = 0x80,
+       RECV_FILTER_MATCH_TYPE_NE,
+};
+
+enum COALESCE_PACKET_TYPE {
+       PACKET_TYPE_UNICAST = 1,
+       PACKET_TYPE_MULTICAST = 2,
+       PACKET_TYPE_BROADCAST = 3
+};
+
+#define MWIFIEX_COALESCE_MAX_RULES     8
+#define MWIFIEX_COALESCE_MAX_BYTESEQ   4       /* non-adjustable */
+#define MWIFIEX_COALESCE_MAX_FILTERS   4
+#define MWIFIEX_MAX_COALESCING_DELAY   100     /* in msecs */
+
+struct filt_field_param {
+       u8 operation;
+       u8 operand_len;
+       u16 offset;
+       u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ];
+};
+
+struct mwifiex_coalesce_rule {
+       u16 max_coalescing_delay;
+       u8 num_of_fields;
+       u8 pkt_type;
+       struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS];
+};
+
+struct mwifiex_ds_coalesce_cfg {
+       u16 num_of_rules;
+       struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
+};
+
 #endif /* !_MWIFIEX_IOCTL_H_ */
index 12e7781..9d7c0e6 100644 (file)
@@ -1427,6 +1427,7 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac)
 
        switch (priv->bss_mode) {
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                return mwifiex_deauthenticate_infra(priv, mac);
        case NL80211_IFTYPE_ADHOC:
                return mwifiex_send_cmd_sync(priv,
index 1753431..fd77833 100644 (file)
@@ -191,12 +191,16 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter)
 {
        s32 i;
 
+       if (adapter->if_ops.cleanup_if)
+               adapter->if_ops.cleanup_if(adapter);
+
        del_timer(&adapter->cmd_timer);
 
        /* Free private structures */
        for (i = 0; i < adapter->priv_num; i++) {
                if (adapter->priv[i]) {
                        mwifiex_free_curr_bcn(adapter->priv[i]);
+                       del_timer_sync(&adapter->priv[i]->scan_delay_timer);
                        kfree(adapter->priv[i]);
                }
        }
@@ -386,6 +390,17 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
 }
 
 /*
+ * This function cancels all works in the queue and destroys
+ * the main workqueue.
+ */
+static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
+{
+       flush_workqueue(adapter->workqueue);
+       destroy_workqueue(adapter->workqueue);
+       adapter->workqueue = NULL;
+}
+
+/*
  * This function gets firmware and initializes it.
  *
  * The main initialization steps followed are -
@@ -394,16 +409,18 @@ static void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
  */
 static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
 {
-       int ret;
+       int ret, i;
        char fmt[64];
        struct mwifiex_private *priv;
        struct mwifiex_adapter *adapter = context;
        struct mwifiex_fw_image fw;
+       struct semaphore *sem = adapter->card_sem;
+       bool init_failed = false;
 
        if (!firmware) {
                dev_err(adapter->dev,
                        "Failed to get firmware %s\n", adapter->fw_name);
-               goto done;
+               goto err_dnld_fw;
        }
 
        memset(&fw, 0, sizeof(struct mwifiex_fw_image));
@@ -416,7 +433,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        else
                ret = mwifiex_dnld_fw(adapter, &fw);
        if (ret == -1)
-               goto done;
+               goto err_dnld_fw;
 
        dev_notice(adapter->dev, "WLAN FW is active\n");
 
@@ -428,13 +445,15 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        }
 
        /* enable host interrupt after fw dnld is successful */
-       if (adapter->if_ops.enable_int)
-               adapter->if_ops.enable_int(adapter);
+       if (adapter->if_ops.enable_int) {
+               if (adapter->if_ops.enable_int(adapter))
+                       goto err_dnld_fw;
+       }
 
        adapter->init_wait_q_woken = false;
        ret = mwifiex_init_fw(adapter);
        if (ret == -1) {
-               goto done;
+               goto err_init_fw;
        } else if (!ret) {
                adapter->hw_status = MWIFIEX_HW_STATUS_READY;
                goto done;
@@ -443,12 +462,12 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        wait_event_interruptible(adapter->init_wait_q,
                                 adapter->init_wait_q_woken);
        if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
-               goto done;
+               goto err_init_fw;
 
        priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
        if (mwifiex_register_cfg80211(adapter)) {
                dev_err(adapter->dev, "cannot register with cfg80211\n");
-               goto err_init_fw;
+               goto err_register_cfg80211;
        }
 
        rtnl_lock();
@@ -458,20 +477,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
                dev_err(adapter->dev, "cannot create default STA interface\n");
                goto err_add_intf;
        }
-
-       /* Create AP interface by default */
-       if (!mwifiex_add_virtual_intf(adapter->wiphy, "uap%d",
-                                     NL80211_IFTYPE_AP, NULL, NULL)) {
-               dev_err(adapter->dev, "cannot create default AP interface\n");
-               goto err_add_intf;
-       }
-
-       /* Create P2P interface by default */
-       if (!mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d",
-                                     NL80211_IFTYPE_P2P_CLIENT, NULL, NULL)) {
-               dev_err(adapter->dev, "cannot create default P2P interface\n");
-               goto err_add_intf;
-       }
        rtnl_unlock();
 
        mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
@@ -479,20 +484,52 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        goto done;
 
 err_add_intf:
-       mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+       for (i = 0; i < adapter->priv_num; i++) {
+               priv = adapter->priv[i];
+
+               if (!priv)
+                       continue;
+
+               if (priv->wdev && priv->netdev)
+                       mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+       }
        rtnl_unlock();
+err_register_cfg80211:
+       wiphy_unregister(adapter->wiphy);
+       wiphy_free(adapter->wiphy);
 err_init_fw:
        if (adapter->if_ops.disable_int)
                adapter->if_ops.disable_int(adapter);
+err_dnld_fw:
        pr_debug("info: %s: unregister device\n", __func__);
-       adapter->if_ops.unregister_dev(adapter);
+       if (adapter->if_ops.unregister_dev)
+               adapter->if_ops.unregister_dev(adapter);
+
+       if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
+           (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
+               pr_debug("info: %s: shutdown mwifiex\n", __func__);
+               adapter->init_wait_q_woken = false;
+
+               if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+                       wait_event_interruptible(adapter->init_wait_q,
+                                                adapter->init_wait_q_woken);
+       }
+       adapter->surprise_removed = true;
+       mwifiex_terminate_workqueue(adapter);
+       init_failed = true;
 done:
        if (adapter->cal_data) {
                release_firmware(adapter->cal_data);
                adapter->cal_data = NULL;
        }
-       release_firmware(adapter->firmware);
+       if (adapter->firmware) {
+               release_firmware(adapter->firmware);
+               adapter->firmware = NULL;
+       }
        complete(&adapter->fw_load);
+       if (init_failed)
+               mwifiex_free_adapter(adapter);
+       up(sem);
        return;
 }
 
@@ -803,18 +840,6 @@ static void mwifiex_main_work_queue(struct work_struct *work)
 }
 
 /*
- * This function cancels all works in the queue and destroys
- * the main workqueue.
- */
-static void
-mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
-{
-       flush_workqueue(adapter->workqueue);
-       destroy_workqueue(adapter->workqueue);
-       adapter->workqueue = NULL;
-}
-
-/*
  * This function adds the card.
  *
  * This function follows the following major steps to set up the device -
@@ -842,6 +867,7 @@ mwifiex_add_card(void *card, struct semaphore *sem,
        }
 
        adapter->iface_type = iface_type;
+       adapter->card_sem = sem;
 
        adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
        adapter->surprise_removed = false;
@@ -872,17 +898,12 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                goto err_init_fw;
        }
 
-       up(sem);
        return 0;
 
 err_init_fw:
        pr_debug("info: %s: unregister device\n", __func__);
        if (adapter->if_ops.unregister_dev)
                adapter->if_ops.unregister_dev(adapter);
-err_registerdev:
-       adapter->surprise_removed = true;
-       mwifiex_terminate_workqueue(adapter);
-err_kmalloc:
        if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
            (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
                pr_debug("info: %s: shutdown mwifiex\n", __func__);
@@ -892,7 +913,10 @@ err_kmalloc:
                        wait_event_interruptible(adapter->init_wait_q,
                                                 adapter->init_wait_q_woken);
        }
-
+err_registerdev:
+       adapter->surprise_removed = true;
+       mwifiex_terminate_workqueue(adapter);
+err_kmalloc:
        mwifiex_free_adapter(adapter);
 
 err_init_sw:
index 253e0bd..1d72f13 100644 (file)
@@ -204,11 +204,11 @@ struct mwifiex_ra_list_tbl {
        struct list_head list;
        struct sk_buff_head skb_head;
        u8 ra[ETH_ALEN];
-       u32 total_pkts_size;
        u32 is_11n_enabled;
        u16 max_amsdu;
-       u16 pkt_count;
+       u16 ba_pkt_count;
        u8 ba_packet_thr;
+       u16 total_pkt_count;
 };
 
 struct mwifiex_tid_tbl {
@@ -515,6 +515,8 @@ struct mwifiex_private {
        bool scan_aborting;
        u8 csa_chan;
        unsigned long csa_expire_time;
+       u8 del_list_idx;
+       bool hs2_enabled;
 };
 
 enum mwifiex_ba_status {
@@ -748,6 +750,7 @@ struct mwifiex_adapter {
 
        atomic_t is_tx_received;
        atomic_t pending_bridged_pkts;
+       struct semaphore *card_sem;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -900,6 +903,8 @@ int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask,
 u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv,
                                    u8 *rates);
 u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates);
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+                                   u8 *rates, u8 radio_type);
 u8 mwifiex_is_rate_auto(struct mwifiex_private *priv);
 extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE];
 void mwifiex_save_curr_bcn(struct mwifiex_private *priv);
@@ -1021,7 +1026,7 @@ mwifiex_netdev_get_priv(struct net_device *dev)
  */
 static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
 {
-       return (*(u32 *)skb->data == PKT_TYPE_MGMT);
+       return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT);
 }
 
 /* This function retrieves channel closed for operation by Channel
index 20c9c4c..52da8ee 100644 (file)
@@ -76,7 +76,7 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
        return false;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /*
  * Kernel needs to suspend all functions separately. Therefore all
  * registered functions must have drivers with suspend and resume
@@ -85,11 +85,12 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
  * If already not suspended, this function allocates and sends a host
  * sleep activate request to the firmware and turns off the traffic.
  */
-static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+static int mwifiex_pcie_suspend(struct device *dev)
 {
        struct mwifiex_adapter *adapter;
        struct pcie_service_card *card;
        int hs_actived;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
                card = (struct pcie_service_card *) pci_get_drvdata(pdev);
@@ -120,10 +121,11 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
  * If already not resumed, this function turns on the traffic and
  * sends a host sleep cancel request to the firmware.
  */
-static int mwifiex_pcie_resume(struct pci_dev *pdev)
+static int mwifiex_pcie_resume(struct device *dev)
 {
        struct mwifiex_adapter *adapter;
        struct pcie_service_card *card;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev) {
                card = (struct pcie_service_card *) pci_get_drvdata(pdev);
@@ -211,9 +213,9 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        wait_for_completion(&adapter->fw_load);
 
        if (user_rmmod) {
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
                if (adapter->is_suspended)
-                       mwifiex_pcie_resume(pdev);
+                       mwifiex_pcie_resume(&pdev->dev);
 #endif
 
                for (i = 0; i < adapter->priv_num; i++)
@@ -233,6 +235,14 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        kfree(card);
 }
 
+static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
+{
+       user_rmmod = 1;
+       mwifiex_pcie_remove(pdev);
+
+       return;
+}
+
 static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
        {
                PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
@@ -249,17 +259,24 @@ static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
 
 MODULE_DEVICE_TABLE(pci, mwifiex_ids);
 
+#ifdef CONFIG_PM_SLEEP
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend,
+                               mwifiex_pcie_resume);
+#endif
+
 /* PCI Device Driver */
 static struct pci_driver __refdata mwifiex_pcie = {
        .name     = "mwifiex_pcie",
        .id_table = mwifiex_ids,
        .probe    = mwifiex_pcie_probe,
        .remove   = mwifiex_pcie_remove,
-#ifdef CONFIG_PM
-       /* Power Management Hooks */
-       .suspend  = mwifiex_pcie_suspend,
-       .resume   = mwifiex_pcie_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver   = {
+               .pm = &mwifiex_pcie_pm_ops,
+       },
 #endif
+       .shutdown = mwifiex_pcie_shutdown,
 };
 
 /*
@@ -1925,7 +1942,7 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
                        ret = 0;
                        break;
                } else {
-                       mdelay(100);
+                       msleep(100);
                        ret = -1;
                }
        }
@@ -1937,12 +1954,10 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num)
                else if (!winner_status) {
                        dev_err(adapter->dev, "PCI-E is the winner\n");
                        adapter->winner = 1;
-                       ret = -1;
                } else {
                        dev_err(adapter->dev,
                                "PCI-E is not the winner <%#x,%d>, exit dnld\n",
                                ret, adapter->winner);
-                       ret = 0;
                }
        }
 
index c447d9b..8cf7d50 100644 (file)
@@ -543,6 +543,37 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
        return chan_idx;
 }
 
+/* This function appends rate TLV to scan config command. */
+static int
+mwifiex_append_rate_tlv(struct mwifiex_private *priv,
+                       struct mwifiex_scan_cmd_config *scan_cfg_out,
+                       u8 radio)
+{
+       struct mwifiex_ie_types_rates_param_set *rates_tlv;
+       u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos;
+       u32 rates_size;
+
+       memset(rates, 0, sizeof(rates));
+
+       tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
+
+       if (priv->scan_request)
+               rates_size = mwifiex_get_rates_from_cfg80211(priv, rates,
+                                                            radio);
+       else
+               rates_size = mwifiex_get_supported_rates(priv, rates);
+
+       dev_dbg(priv->adapter->dev, "info: SCAN_CMD: Rates size = %d\n",
+               rates_size);
+       rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos;
+       rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
+       rates_tlv->header.len = cpu_to_le16((u16) rates_size);
+       memcpy(rates_tlv->rates, rates, rates_size);
+       scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
+
+       return rates_size;
+}
+
 /*
  * This function constructs and sends multiple scan config commands to
  * the firmware.
@@ -564,9 +595,10 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
        struct mwifiex_chan_scan_param_set *tmp_chan_list;
        struct mwifiex_chan_scan_param_set *start_chan;
 
-       u32 tlv_idx;
+       u32 tlv_idx, rates_size;
        u32 total_scan_time;
        u32 done_early;
+       u8 radio_type;
 
        if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) {
                dev_dbg(priv->adapter->dev,
@@ -591,6 +623,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
 
                tlv_idx = 0;
                total_scan_time = 0;
+               radio_type = 0;
                chan_tlv_out->header.len = 0;
                start_chan = tmp_chan_list;
                done_early = false;
@@ -612,6 +645,7 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                                continue;
                        }
 
+                       radio_type = tmp_chan_list->radio_type;
                        dev_dbg(priv->adapter->dev,
                                "info: Scan: Chan(%3d), Radio(%d),"
                                " Mode(%d, %d), Dur(%d)\n",
@@ -692,6 +726,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                        break;
                }
 
+               rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out,
+                                                    radio_type);
+
                priv->adapter->scan_channels = start_chan;
 
                /* Send the scan command to the firmware with the specified
@@ -699,6 +736,14 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN,
                                             HostCmd_ACT_GEN_SET, 0,
                                             scan_cfg_out);
+
+               /* rate IE is updated per scan command but same starting
+                * pointer is used each time so that rate IE from earlier
+                * scan_cfg_out->buf is overwritten with new one.
+                */
+               scan_cfg_out->tlv_buf_len -=
+                           sizeof(struct mwifiex_ie_types_header) + rates_size;
+
                if (ret)
                        break;
        }
@@ -741,7 +786,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
        struct mwifiex_adapter *adapter = priv->adapter;
        struct mwifiex_ie_types_num_probes *num_probes_tlv;
        struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
-       struct mwifiex_ie_types_rates_param_set *rates_tlv;
        u8 *tlv_pos;
        u32 num_probes;
        u32 ssid_len;
@@ -753,8 +797,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
        u8 radio_type;
        int i;
        u8 ssid_filter;
-       u8 rates[MWIFIEX_SUPPORTED_RATES];
-       u32 rates_size;
        struct mwifiex_ie_types_htcap *ht_cap;
 
        /* The tlv_buf_len is calculated for each scan command.  The TLVs added
@@ -889,19 +931,6 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 
        }
 
-       /* Append rates tlv */
-       memset(rates, 0, sizeof(rates));
-
-       rates_size = mwifiex_get_supported_rates(priv, rates);
-
-       rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos;
-       rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
-       rates_tlv->header.len = cpu_to_le16((u16) rates_size);
-       memcpy(rates_tlv->rates, rates, rates_size);
-       tlv_pos += sizeof(rates_tlv->header) + rates_size;
-
-       dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size);
-
        if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
            (priv->adapter->config_bands & BAND_GN ||
             priv->adapter->config_bands & BAND_AN)) {
index 09185c9..1576104 100644 (file)
@@ -50,9 +50,6 @@ static struct mwifiex_if_ops sdio_ops;
 
 static struct semaphore add_remove_card_sem;
 
-static int mwifiex_sdio_resume(struct device *dev);
-static void mwifiex_sdio_interrupt(struct sdio_func *func);
-
 /*
  * SDIO probe.
  *
@@ -113,6 +110,51 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
 }
 
 /*
+ * SDIO resume.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_sdio_resume(struct device *dev)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       struct sdio_mmc_card *card;
+       struct mwifiex_adapter *adapter;
+       mmc_pm_flag_t pm_flag = 0;
+
+       if (func) {
+               pm_flag = sdio_get_host_pm_caps(func);
+               card = sdio_get_drvdata(func);
+               if (!card || !card->adapter) {
+                       pr_err("resume: invalid card or adapter\n");
+                       return 0;
+               }
+       } else {
+               pr_err("resume: sdio_func is not specified\n");
+               return 0;
+       }
+
+       adapter = card->adapter;
+
+       if (!adapter->is_suspended) {
+               dev_warn(adapter->dev, "device already resumed\n");
+               return 0;
+       }
+
+       adapter->is_suspended = false;
+
+       /* Disable Host Sleep */
+       mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+                         MWIFIEX_ASYNC_CMD);
+
+       return 0;
+}
+
+/*
  * SDIO remove.
  *
  * This function removes the interface and frees up the card structure.
@@ -212,51 +254,6 @@ static int mwifiex_sdio_suspend(struct device *dev)
        return ret;
 }
 
-/*
- * SDIO resume.
- *
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_sdio_resume(struct device *dev)
-{
-       struct sdio_func *func = dev_to_sdio_func(dev);
-       struct sdio_mmc_card *card;
-       struct mwifiex_adapter *adapter;
-       mmc_pm_flag_t pm_flag = 0;
-
-       if (func) {
-               pm_flag = sdio_get_host_pm_caps(func);
-               card = sdio_get_drvdata(func);
-               if (!card || !card->adapter) {
-                       pr_err("resume: invalid card or adapter\n");
-                       return 0;
-               }
-       } else {
-               pr_err("resume: sdio_func is not specified\n");
-               return 0;
-       }
-
-       adapter = card->adapter;
-
-       if (!adapter->is_suspended) {
-               dev_warn(adapter->dev, "device already resumed\n");
-               return 0;
-       }
-
-       adapter->is_suspended = false;
-
-       /* Disable Host Sleep */
-       mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
-                         MWIFIEX_ASYNC_CMD);
-
-       return 0;
-}
-
 /* Device ID for SD8786 */
 #define SDIO_DEVICE_ID_MARVELL_8786   (0x9116)
 /* Device ID for SD8787 */
@@ -707,6 +704,65 @@ static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
 }
 
 /*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+       struct sdio_mmc_card *card = adapter->card;
+       u8 sdio_ireg;
+       unsigned long flags;
+
+       if (mwifiex_read_data_sync(adapter, card->mp_regs,
+                                  card->reg->max_mp_regs,
+                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
+               dev_err(adapter->dev, "read mp_regs failed\n");
+               return;
+       }
+
+       sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
+       if (sdio_ireg) {
+               /*
+                * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+                * For SDIO new mode CMD port interrupts
+                *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+                *      UP_LD_CMD_PORT_HOST_INT_STATUS
+                * Clear the interrupt status register
+                */
+               dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
+               spin_lock_irqsave(&adapter->int_lock, flags);
+               adapter->int_status |= sdio_ireg;
+               spin_unlock_irqrestore(&adapter->int_lock, flags);
+       }
+}
+
+/*
+ * SDIO interrupt handler.
+ *
+ * This function reads the interrupt status from firmware and handles
+ * the interrupt in current thread (ksdioirqd) right away.
+ */
+static void
+mwifiex_sdio_interrupt(struct sdio_func *func)
+{
+       struct mwifiex_adapter *adapter;
+       struct sdio_mmc_card *card;
+
+       card = sdio_get_drvdata(func);
+       if (!card || !card->adapter) {
+               pr_debug("int: func=%p card=%p adapter=%p\n",
+                        func, card, card ? card->adapter : NULL);
+               return;
+       }
+       adapter = card->adapter;
+
+       if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
+               adapter->ps_state = PS_STATE_AWAKE;
+
+       mwifiex_interrupt_status(adapter);
+       mwifiex_main_process(adapter);
+}
+
+/*
  * This function enables the host interrupt.
  *
  * The host interrupt enable mask is written to the card
@@ -944,7 +1000,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
                        ret = 0;
                        break;
                } else {
-                       mdelay(100);
+                       msleep(100);
                        ret = -1;
                }
        }
@@ -963,65 +1019,6 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
 }
 
 /*
- * This function reads the interrupt status from card.
- */
-static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
-{
-       struct sdio_mmc_card *card = adapter->card;
-       u8 sdio_ireg;
-       unsigned long flags;
-
-       if (mwifiex_read_data_sync(adapter, card->mp_regs,
-                                  card->reg->max_mp_regs,
-                                  REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
-               dev_err(adapter->dev, "read mp_regs failed\n");
-               return;
-       }
-
-       sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
-       if (sdio_ireg) {
-               /*
-                * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
-                * For SDIO new mode CMD port interrupts
-                *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
-                *      UP_LD_CMD_PORT_HOST_INT_STATUS
-                * Clear the interrupt status register
-                */
-               dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
-               spin_lock_irqsave(&adapter->int_lock, flags);
-               adapter->int_status |= sdio_ireg;
-               spin_unlock_irqrestore(&adapter->int_lock, flags);
-       }
-}
-
-/*
- * SDIO interrupt handler.
- *
- * This function reads the interrupt status from firmware and handles
- * the interrupt in current thread (ksdioirqd) right away.
- */
-static void
-mwifiex_sdio_interrupt(struct sdio_func *func)
-{
-       struct mwifiex_adapter *adapter;
-       struct sdio_mmc_card *card;
-
-       card = sdio_get_drvdata(func);
-       if (!card || !card->adapter) {
-               pr_debug("int: func=%p card=%p adapter=%p\n",
-                        func, card, card ? card->adapter : NULL);
-               return;
-       }
-       adapter = card->adapter;
-
-       if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
-               adapter->ps_state = PS_STATE_AWAKE;
-
-       mwifiex_interrupt_status(adapter);
-       mwifiex_main_process(adapter);
-}
-
-/*
  * This function decodes a received packet.
  *
  * Based on the type, the packet is treated as either a data, or
@@ -1065,7 +1062,7 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
 
        case MWIFIEX_TYPE_EVENT:
                dev_dbg(adapter->dev, "info: --- Rx: Event ---\n");
-               adapter->event_cause = *(u32 *) skb->data;
+               adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data);
 
                if ((skb->len > 0) && (skb->len  < MAX_EVENT_SIZE))
                        memcpy(adapter->event_body,
@@ -1210,8 +1207,8 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
                for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
 
                        /* get curr PKT len & type */
-                       pkt_len = *(u16 *) &curr_ptr[0];
-                       pkt_type = *(u16 *) &curr_ptr[2];
+                       pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
+                       pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
 
                        /* copy pkt to deaggr buf */
                        skb_deaggr = card->mpa_rx.skb_arr[pind];
index 8ece485..c0268b5 100644 (file)
@@ -707,8 +707,9 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv,
                if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
                        tlv_mac = (void *)((u8 *)&key_material->key_param_set +
                                           key_param_len);
-                       tlv_mac->tlv.type = cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
-                       tlv_mac->tlv.len = cpu_to_le16(ETH_ALEN);
+                       tlv_mac->header.type =
+                                       cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
+                       tlv_mac->header.len = cpu_to_le16(ETH_ALEN);
                        memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN);
                        cmd_size = key_param_len + S_DS_GEN +
                                   sizeof(key_material->action) +
@@ -1069,7 +1070,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv,
        int i, byte_len;
        u8 *stack_ptr = *buffer;
 
-       for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) {
+       for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) {
                filter = &mef_entry->filter[i];
                if (!filter->filt_type)
                        break;
@@ -1078,7 +1079,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv,
                *stack_ptr = TYPE_DNUM;
                stack_ptr += 1;
 
-               byte_len = filter->byte_seq[MAX_BYTESEQ];
+               byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ];
                memcpy(stack_ptr, filter->byte_seq, byte_len);
                stack_ptr += byte_len;
                *stack_ptr = byte_len;
@@ -1183,6 +1184,70 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
        return 0;
 }
 
+static int
+mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
+                        struct host_cmd_ds_command *cmd,
+                        u16 cmd_action, void *data_buf)
+{
+       struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
+                                               &cmd->params.coalesce_cfg;
+       struct mwifiex_ds_coalesce_cfg *cfg = data_buf;
+       struct coalesce_filt_field_param *param;
+       u16 cnt, idx, length;
+       struct coalesce_receive_filt_rule *rule;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG);
+       cmd->size = cpu_to_le16(S_DS_GEN);
+
+       coalesce_cfg->action = cpu_to_le16(cmd_action);
+       coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
+       rule = coalesce_cfg->rule;
+
+       for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
+               rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
+               rule->max_coalescing_delay =
+                       cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
+               rule->pkt_type = cfg->rule[cnt].pkt_type;
+               rule->num_of_fields = cfg->rule[cnt].num_of_fields;
+
+               length = 0;
+
+               param = rule->params;
+               for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
+                       param->operation = cfg->rule[cnt].params[idx].operation;
+                       param->operand_len =
+                                       cfg->rule[cnt].params[idx].operand_len;
+                       param->offset =
+                               cpu_to_le16(cfg->rule[cnt].params[idx].offset);
+                       memcpy(param->operand_byte_stream,
+                              cfg->rule[cnt].params[idx].operand_byte_stream,
+                              param->operand_len);
+
+                       length += sizeof(struct coalesce_filt_field_param);
+
+                       param++;
+               }
+
+               /* Total rule length is sizeof max_coalescing_delay(u16),
+                * num_of_fields(u8), pkt_type(u8) and total length of the all
+                * params
+                */
+               rule->header.len = cpu_to_le16(length + sizeof(u16) +
+                                              sizeof(u8) + sizeof(u8));
+
+               /* Add the rule length to the command size*/
+               le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) +
+                            sizeof(struct mwifiex_ie_types_header));
+
+               rule = (void *)((u8 *)rule->params + length);
+       }
+
+       /* Add sizeof action, num_of_rules to total command length */
+       le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
@@ -1406,6 +1471,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_MEF_CFG:
                ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
                break;
+       case HostCmd_CMD_COALESCE_CFG:
+               ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
+                                              data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
index d85df15..58a6013 100644 (file)
@@ -280,7 +280,7 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
 
        tlv_buf = ((u8 *)rate_cfg) +
                        sizeof(struct host_cmd_ds_tx_rate_cfg);
-       tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16));
+       tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16)));
 
        while (tlv_buf && tlv_buf_len > 0) {
                tlv = (*tlv_buf);
@@ -997,6 +997,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                break;
        case HostCmd_CMD_MEF_CFG:
                break;
+       case HostCmd_CMD_COALESCE_CFG:
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);
index ea265ec..8b05752 100644 (file)
@@ -201,6 +201,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
        case EVENT_DEAUTHENTICATED:
                dev_dbg(adapter->dev, "event: Deauthenticated\n");
+               if (priv->wps.session_enable) {
+                       dev_dbg(adapter->dev,
+                               "info: receive deauth event in wps session\n");
+                       break;
+               }
                adapter->dbg.num_event_deauth++;
                if (priv->media_connected) {
                        reason_code =
@@ -211,6 +216,11 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
        case EVENT_DISASSOCIATED:
                dev_dbg(adapter->dev, "event: Disassociated\n");
+               if (priv->wps.session_enable) {
+                       dev_dbg(adapter->dev,
+                               "info: receive disassoc event in wps session\n");
+                       break;
+               }
                adapter->dbg.num_event_disassoc++;
                if (priv->media_connected) {
                        reason_code =
index 8af97ab..f084412 100644 (file)
@@ -797,15 +797,16 @@ static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
                               u8 *ie_data_ptr, u16 ie_len)
 {
        if (ie_len) {
-               priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
-               if (!priv->wps_ie)
-                       return -ENOMEM;
-               if (ie_len > sizeof(priv->wps_ie)) {
+               if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
                        dev_dbg(priv->adapter->dev,
                                "info: failed to copy WPS IE, too big\n");
-                       kfree(priv->wps_ie);
                        return -1;
                }
+
+               priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
+               if (!priv->wps_ie)
+                       return -ENOMEM;
+
                memcpy(priv->wps_ie, ie_data_ptr, ie_len);
                priv->wps_ie_len = ie_len;
                dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n",
index b5c1095..bb22664 100644 (file)
@@ -17,6 +17,8 @@
  * this warranty disclaimer.
  */
 
+#include <uapi/linux/ipv6.h>
+#include <net/ndisc.h>
 #include "decl.h"
 #include "ioctl.h"
 #include "util.h"
 #include "11n_aggr.h"
 #include "11n_rxreorder.h"
 
+/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
+ * frame. If frame has both source and destination mac address as same, this
+ * function drops such gratuitous frames.
+ */
+static bool
+mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
+                              struct sk_buff *skb)
+{
+       const struct mwifiex_arp_eth_header *arp;
+       struct ethhdr *eth_hdr;
+       struct ipv6hdr *ipv6;
+       struct icmp6hdr *icmpv6;
+
+       eth_hdr = (struct ethhdr *)skb->data;
+       switch (ntohs(eth_hdr->h_proto)) {
+       case ETH_P_ARP:
+               arp = (void *)(skb->data + sizeof(struct ethhdr));
+               if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
+                   arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
+                       if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
+                               return true;
+               }
+               break;
+       case ETH_P_IPV6:
+               ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
+               icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
+                                 sizeof(struct ipv6hdr));
+               if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
+                       if (!memcmp(&ipv6->saddr, &ipv6->daddr,
+                                   sizeof(struct in6_addr)))
+                               return true;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 /*
  * This function processes the received packet and forwards it
  * to kernel/upper layer.
@@ -90,6 +132,13 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
           either the reconstructed EthII frame or the 802.2/llc/snap frame */
        skb_pull(skb, hdr_chop);
 
+       if (priv->hs2_enabled &&
+           mwifiex_discard_gratuitous_arp(priv, skb)) {
+               dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n");
+               dev_kfree_skb_any(skb);
+               return 0;
+       }
+
        priv->rxpd_rate = local_rx_pd->rx_rate;
 
        priv->rxpd_htinfo = local_rx_pd->ht_info;
index 2de882d..64424c8 100644 (file)
@@ -293,9 +293,9 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
        u8 *tlv = *tlv_buf;
 
        tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
-       tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
-       tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
-                                       sizeof(struct host_cmd_tlv));
+       tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
+       tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
+                                       sizeof(struct mwifiex_ie_types_header));
        tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
        tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
        cmd_size += sizeof(struct host_cmd_tlv_akmp);
@@ -303,10 +303,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
                pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
-               pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
-               pwk_cipher->tlv.len =
+               pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+               pwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
                pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
                cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
@@ -315,10 +315,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
                pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
-               pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
-               pwk_cipher->tlv.len =
+               pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+               pwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
                pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
                cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
@@ -327,10 +327,10 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
                gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
-               gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
-               gwk_cipher->tlv.len =
+               gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
+               gwk_cipher->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
                cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
                tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
@@ -338,13 +338,15 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->wpa_cfg.length) {
                passphrase = (struct host_cmd_tlv_passphrase *)tlv;
-               passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
-               passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
+               passphrase->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
+               passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
                memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
                       bss_cfg->wpa_cfg.length);
-               cmd_size += sizeof(struct host_cmd_tlv) +
+               cmd_size += sizeof(struct mwifiex_ie_types_header) +
                            bss_cfg->wpa_cfg.length;
-               tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length;
+               tlv += sizeof(struct mwifiex_ie_types_header) +
+                               bss_cfg->wpa_cfg.length;
        }
 
        *param_size = cmd_size;
@@ -403,16 +405,17 @@ mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
                    (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
                     bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
                        wep_key = (struct host_cmd_tlv_wep_key *)tlv;
-                       wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
-                       wep_key->tlv.len =
+                       wep_key->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
+                       wep_key->header.len =
                                cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
                        wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
                        wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
                        memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
                               bss_cfg->wep_cfg[i].length);
-                       cmd_size += sizeof(struct host_cmd_tlv) + 2 +
+                       cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
                                    bss_cfg->wep_cfg[i].length;
-                       tlv += sizeof(struct host_cmd_tlv) + 2 +
+                       tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
                                    bss_cfg->wep_cfg[i].length;
                }
        }
@@ -449,16 +452,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->ssid.ssid_len) {
                ssid = (struct host_cmd_tlv_ssid *)tlv;
-               ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
-               ssid->tlv.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
+               ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
+               ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
                memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
-               cmd_size += sizeof(struct host_cmd_tlv) +
+               cmd_size += sizeof(struct mwifiex_ie_types_header) +
                            bss_cfg->ssid.ssid_len;
-               tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len;
+               tlv += sizeof(struct mwifiex_ie_types_header) +
+                               bss_cfg->ssid.ssid_len;
 
                bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
-               bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
-               bcast_ssid->tlv.len =
+               bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
+               bcast_ssid->header.len =
                                cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
                bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
                cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
@@ -466,13 +470,13 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        }
        if (bss_cfg->rates[0]) {
                tlv_rates = (struct host_cmd_tlv_rates *)tlv;
-               tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
+               tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
 
                for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
                     i++)
                        tlv_rates->rates[i] = bss_cfg->rates[i];
 
-               tlv_rates->tlv.len = cpu_to_le16(i);
+               tlv_rates->header.len = cpu_to_le16(i);
                cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
                tlv += sizeof(struct host_cmd_tlv_rates) + i;
        }
@@ -482,10 +486,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
            (bss_cfg->band_cfg == BAND_CONFIG_A &&
             bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
                chan_band = (struct host_cmd_tlv_channel_band *)tlv;
-               chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
-               chan_band->tlv.len =
+               chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+               chan_band->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                chan_band->band_config = bss_cfg->band_cfg;
                chan_band->channel = bss_cfg->channel;
                cmd_size += sizeof(struct host_cmd_tlv_channel_band);
@@ -494,11 +498,11 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
            bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
                beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
-               beacon_period->tlv.type =
+               beacon_period->header.type =
                                        cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
-               beacon_period->tlv.len =
+               beacon_period->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
                cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
                tlv += sizeof(struct host_cmd_tlv_beacon_period);
@@ -506,21 +510,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
            bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
                dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
-               dtim_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
-               dtim_period->tlv.len =
+               dtim_period->header.type =
+                       cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
+               dtim_period->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                dtim_period->period = bss_cfg->dtim_period;
                cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
                tlv += sizeof(struct host_cmd_tlv_dtim_period);
        }
        if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
                rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
-               rts_threshold->tlv.type =
+               rts_threshold->header.type =
                                        cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
-               rts_threshold->tlv.len =
+               rts_threshold->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
                cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
                tlv += sizeof(struct host_cmd_tlv_frag_threshold);
@@ -528,21 +533,22 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
            (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
                frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
-               frag_threshold->tlv.type =
+               frag_threshold->header.type =
                                cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
-               frag_threshold->tlv.len =
+               frag_threshold->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
                cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
                tlv += sizeof(struct host_cmd_tlv_frag_threshold);
        }
        if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
                retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
-               retry_limit->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
-               retry_limit->tlv.len =
+               retry_limit->header.type =
+                       cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
+               retry_limit->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
-                                   sizeof(struct host_cmd_tlv));
+                                   sizeof(struct mwifiex_ie_types_header));
                retry_limit->limit = (u8)bss_cfg->retry_limit;
                cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
                tlv += sizeof(struct host_cmd_tlv_retry_limit);
@@ -557,21 +563,21 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
        if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
            (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
                auth_type = (struct host_cmd_tlv_auth_type *)tlv;
-               auth_type->tlv.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
-               auth_type->tlv.len =
+               auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+               auth_type->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
-                       sizeof(struct host_cmd_tlv));
+                       sizeof(struct mwifiex_ie_types_header));
                auth_type->auth_type = (u8)bss_cfg->auth_mode;
                cmd_size += sizeof(struct host_cmd_tlv_auth_type);
                tlv += sizeof(struct host_cmd_tlv_auth_type);
        }
        if (bss_cfg->protocol) {
                encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
-               encrypt_protocol->tlv.type =
+               encrypt_protocol->header.type =
                        cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
-               encrypt_protocol->tlv.len =
+               encrypt_protocol->header.len =
                        cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
-                       - sizeof(struct host_cmd_tlv));
+                       - sizeof(struct mwifiex_ie_types_header));
                encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
                cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
                tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
@@ -608,9 +614,9 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->sta_ao_timer) {
                ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
-               ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
-               ao_timer->tlv.len = cpu_to_le16(sizeof(*ao_timer) -
-                                               sizeof(struct host_cmd_tlv));
+               ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
+               ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
+                                       sizeof(struct mwifiex_ie_types_header));
                ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
                cmd_size += sizeof(*ao_timer);
                tlv += sizeof(*ao_timer);
@@ -618,9 +624,10 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 
        if (bss_cfg->ps_sta_ao_timer) {
                ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
-               ps_ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
-               ps_ao_timer->tlv.len = cpu_to_le16(sizeof(*ps_ao_timer) -
-                                                  sizeof(struct host_cmd_tlv));
+               ps_ao_timer->header.type =
+                               cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
+               ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
+                               sizeof(struct mwifiex_ie_types_header));
                ps_ao_timer->sta_ao_timer =
                                        cpu_to_le32(bss_cfg->ps_sta_ao_timer);
                cmd_size += sizeof(*ps_ao_timer);
@@ -636,16 +643,17 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
 static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
 {
        struct mwifiex_ie_list *ap_ie = cmd_buf;
-       struct host_cmd_tlv *tlv_ie = (struct host_cmd_tlv *)tlv;
+       struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
 
        if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
                return -1;
 
-       *ie_size += le16_to_cpu(ap_ie->len) + sizeof(struct host_cmd_tlv);
+       *ie_size += le16_to_cpu(ap_ie->len) +
+                       sizeof(struct mwifiex_ie_types_header);
 
        tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
        tlv_ie->len = ap_ie->len;
-       tlv += sizeof(struct host_cmd_tlv);
+       tlv += sizeof(struct mwifiex_ie_types_header);
 
        memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
 
index a018e42..1cfe5a7 100644 (file)
 #include "11n_aggr.h"
 #include "11n_rxreorder.h"
 
+/* This function checks if particular RA list has packets more than low bridge
+ * packet threshold and then deletes packet from this RA list.
+ * Function deletes packets from such RA list and returns true. If no such list
+ * is found, false is returned.
+ */
+static bool
+mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
+                                 struct list_head *ra_list_head)
+{
+       struct mwifiex_ra_list_tbl *ra_list;
+       struct sk_buff *skb, *tmp;
+       bool pkt_deleted = false;
+       struct mwifiex_txinfo *tx_info;
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       list_for_each_entry(ra_list, ra_list_head, list) {
+               if (skb_queue_empty(&ra_list->skb_head))
+                       continue;
+
+               skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+                       tx_info = MWIFIEX_SKB_TXCB(skb);
+                       if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
+                               __skb_unlink(skb, &ra_list->skb_head);
+                               mwifiex_write_data_complete(adapter, skb, 0,
+                                                           -1);
+                               atomic_dec(&priv->wmm.tx_pkts_queued);
+                               pkt_deleted = true;
+                       }
+                       if ((atomic_read(&adapter->pending_bridged_pkts) <=
+                                            MWIFIEX_BRIDGED_PKTS_THR_LOW))
+                               break;
+               }
+       }
+
+       return pkt_deleted;
+}
+
+/* This function deletes packets from particular RA List. RA list index
+ * from which packets are deleted is preserved so that packets from next RA
+ * list are deleted upon subsequent call thus maintaining fairness.
+ */
+static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
+{
+       unsigned long flags;
+       struct list_head *ra_list;
+       int i;
+
+       spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+       for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
+               if (priv->del_list_idx == MAX_NUM_TID)
+                       priv->del_list_idx = 0;
+               ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
+               if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) {
+                       priv->del_list_idx++;
+                       break;
+               }
+       }
+
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+
 static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
                                         struct sk_buff *skb)
 {
@@ -40,10 +103,11 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
 
        if ((atomic_read(&adapter->pending_bridged_pkts) >=
-                                            MWIFIEX_BRIDGED_PKTS_THRESHOLD)) {
+                                            MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
                dev_err(priv->adapter->dev,
                        "Tx: Bridge packet limit reached. Drop packet!\n");
                kfree_skb(skb);
+               mwifiex_uap_cleanup_tx_queues(priv);
                return;
        }
 
@@ -95,10 +159,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        atomic_inc(&adapter->tx_pending);
        atomic_inc(&adapter->pending_bridged_pkts);
 
-       if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) {
-               mwifiex_set_trans_start(priv->netdev);
-               mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
-       }
        return;
 }
 
index f90fe21..2472d4b 100644 (file)
@@ -24,9 +24,9 @@
 
 static const char usbdriver_name[] = "usb8797";
 
-static u8 user_rmmod;
 static struct mwifiex_if_ops usb_ops;
 static struct semaphore add_remove_card_sem;
+static struct usb_card_rec *usb_card;
 
 static struct usb_device_id mwifiex_usb_table[] = {
        {USB_DEVICE(USB8797_VID, USB8797_PID_1)},
@@ -350,6 +350,7 @@ static int mwifiex_usb_probe(struct usb_interface *intf,
 
        card->udev = udev;
        card->intf = intf;
+       usb_card = card;
 
        pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n",
                 udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass,
@@ -532,7 +533,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf)
 {
        struct usb_card_rec *card = usb_get_intfdata(intf);
        struct mwifiex_adapter *adapter;
-       int i;
 
        if (!card || !card->adapter) {
                pr_err("%s: card or card->adapter is NULL\n", __func__);
@@ -543,27 +543,6 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf)
        if (!adapter->priv_num)
                return;
 
-       /* In case driver is removed when asynchronous FW downloading is
-        * in progress
-        */
-       wait_for_completion(&adapter->fw_load);
-
-       if (user_rmmod) {
-#ifdef CONFIG_PM
-               if (adapter->is_suspended)
-                       mwifiex_usb_resume(intf);
-#endif
-               for (i = 0; i < adapter->priv_num; i++)
-                       if ((GET_BSS_ROLE(adapter->priv[i]) ==
-                            MWIFIEX_BSS_ROLE_STA) &&
-                           adapter->priv[i]->media_connected)
-                               mwifiex_deauthenticate(adapter->priv[i], NULL);
-
-               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
-                                                         MWIFIEX_BSS_ROLE_ANY),
-                                        MWIFIEX_FUNC_SHUTDOWN);
-       }
-
        mwifiex_usb_free(card);
 
        dev_dbg(adapter->dev, "%s: removing card\n", __func__);
@@ -786,6 +765,13 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        return 0;
 }
 
+static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+
+       usb_set_intfdata(card->intf, NULL);
+}
+
 static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
                                    struct mwifiex_fw_image *fw)
 {
@@ -978,6 +964,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
 
 static struct mwifiex_if_ops usb_ops = {
        .register_dev =         mwifiex_register_dev,
+       .unregister_dev =       mwifiex_unregister_dev,
        .wakeup =               mwifiex_pm_wakeup_card,
        .wakeup_complete =      mwifiex_pm_wakeup_card_complete,
 
@@ -1024,8 +1011,29 @@ static void mwifiex_usb_cleanup_module(void)
        if (!down_interruptible(&add_remove_card_sem))
                up(&add_remove_card_sem);
 
-       /* set the flag as user is removing this module */
-       user_rmmod = 1;
+       if (usb_card) {
+               struct mwifiex_adapter *adapter = usb_card->adapter;
+               int i;
+
+               /* In case driver is removed when asynchronous FW downloading is
+                * in progress
+                */
+               wait_for_completion(&adapter->fw_load);
+
+#ifdef CONFIG_PM
+               if (adapter->is_suspended)
+                       mwifiex_usb_resume(usb_card->intf);
+#endif
+               for (i = 0; i < adapter->priv_num; i++)
+                       if ((GET_BSS_ROLE(adapter->priv[i]) ==
+                            MWIFIEX_BSS_ROLE_STA) &&
+                           adapter->priv[i]->media_connected)
+                               mwifiex_deauthenticate(adapter->priv[i], NULL);
+
+               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
+                                                         MWIFIEX_BSS_ROLE_ANY),
+                                        MWIFIEX_FUNC_SHUTDOWN);
+       }
 
        usb_deregister(&mwifiex_usb_driver);
 }
index 944e884..2e8f9cd 100644 (file)
@@ -120,7 +120,7 @@ mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra)
 
        memcpy(ra_list->ra, ra, ETH_ALEN);
 
-       ra_list->total_pkts_size = 0;
+       ra_list->total_pkt_count = 0;
 
        dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list);
 
@@ -188,7 +188,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
                        ra_list, ra_list->is_11n_enabled);
 
                if (ra_list->is_11n_enabled) {
-                       ra_list->pkt_count = 0;
+                       ra_list->ba_pkt_count = 0;
                        ra_list->ba_packet_thr =
                                              mwifiex_get_random_ba_threshold();
                }
@@ -679,8 +679,8 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
 
        skb_queue_tail(&ra_list->skb_head, skb);
 
-       ra_list->total_pkts_size += skb->len;
-       ra_list->pkt_count++;
+       ra_list->ba_pkt_count++;
+       ra_list->total_pkt_count++;
 
        if (atomic_read(&priv->wmm.highest_queued_prio) <
                                                tos_to_tid_inv[tid_down])
@@ -1037,7 +1037,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv,
        tx_info = MWIFIEX_SKB_TXCB(skb);
        dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb);
 
-       ptr->total_pkts_size -= skb->len;
+       ptr->total_pkt_count--;
 
        if (!skb_queue_empty(&ptr->skb_head))
                skb_next = skb_peek(&ptr->skb_head);
@@ -1062,8 +1062,8 @@ mwifiex_send_single_packet(struct mwifiex_private *priv,
 
                skb_queue_tail(&ptr->skb_head, skb);
 
-               ptr->total_pkts_size += skb->len;
-               ptr->pkt_count++;
+               ptr->total_pkt_count++;
+               ptr->ba_pkt_count++;
                tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
                spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
                                       ra_list_flags);
@@ -1224,7 +1224,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                   mwifiex_send_single_packet() */
        } else {
                if (mwifiex_is_ampdu_allowed(priv, tid) &&
-                   ptr->pkt_count > ptr->ba_packet_thr) {
+                   ptr->ba_pkt_count > ptr->ba_packet_thr) {
                        if (mwifiex_space_avail_for_new_ba_stream(adapter)) {
                                mwifiex_create_ba_tbl(priv, ptr->ra, tid,
                                                      BA_SETUP_INPROGRESS);
index 3e60a31..68dbbb9 100644 (file)
@@ -166,6 +166,12 @@ config RT2800USB_RT35XX
          rt2800usb driver.
          Supported chips: RT3572
 
+config RT2800USB_RT3573
+       bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)"
+       ---help---
+         This enables support for RT3573 chipset based wireless USB devices
+         in the rt2800usb driver.
+
 config RT2800USB_RT53XX
        bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)"
        ---help---
index d78c495..fa33b5e 100644 (file)
@@ -88,6 +88,7 @@
 #define REV_RT3071E                    0x0211
 #define REV_RT3090E                    0x0211
 #define REV_RT3390E                    0x0211
+#define REV_RT3593E                    0x0211
 #define REV_RT5390F                    0x0502
 #define REV_RT5390R                    0x1502
 #define REV_RT5592C                    0x0221
 #define TX_PWR_CFG_0_9MBS              FIELD32(0x00f00000)
 #define TX_PWR_CFG_0_12MBS             FIELD32(0x0f000000)
 #define TX_PWR_CFG_0_18MBS             FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_0_CCK1_CH0          FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_CCK1_CH1          FIELD32(0x000000f0)
+#define TX_PWR_CFG_0_CCK5_CH0          FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_CCK5_CH1          FIELD32(0x0000f000)
+#define TX_PWR_CFG_0_OFDM6_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_OFDM6_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_0_OFDM12_CH0                FIELD32(0x0f000000)
+#define TX_PWR_CFG_0_OFDM12_CH1                FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_1:
 #define TX_PWR_CFG_1_MCS1              FIELD32(0x00f00000)
 #define TX_PWR_CFG_1_MCS2              FIELD32(0x0f000000)
 #define TX_PWR_CFG_1_MCS3              FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_1_OFDM24_CH0                FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_OFDM24_CH1                FIELD32(0x000000f0)
+#define TX_PWR_CFG_1_OFDM48_CH0                FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_OFDM48_CH1                FIELD32(0x0000f000)
+#define TX_PWR_CFG_1_MCS0_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_MCS0_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_1_MCS2_CH0          FIELD32(0x0f000000)
+#define TX_PWR_CFG_1_MCS2_CH1          FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_2:
 #define TX_PWR_CFG_2_MCS9              FIELD32(0x00f00000)
 #define TX_PWR_CFG_2_MCS10             FIELD32(0x0f000000)
 #define TX_PWR_CFG_2_MCS11             FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_2_MCS4_CH0          FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_MCS4_CH1          FIELD32(0x000000f0)
+#define TX_PWR_CFG_2_MCS6_CH0          FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_MCS6_CH1          FIELD32(0x0000f000)
+#define TX_PWR_CFG_2_MCS8_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_MCS8_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_2_MCS10_CH0         FIELD32(0x0f000000)
+#define TX_PWR_CFG_2_MCS10_CH1         FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_3:
 #define TX_PWR_CFG_3_UKNOWN2           FIELD32(0x00f00000)
 #define TX_PWR_CFG_3_UKNOWN3           FIELD32(0x0f000000)
 #define TX_PWR_CFG_3_UKNOWN4           FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_MCS12_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_MCS12_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_MCS14_CH0         FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_MCS14_CH1         FIELD32(0x0000f000)
+#define TX_PWR_CFG_3_STBC0_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_STBC0_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_3_STBC2_CH0         FIELD32(0x0f000000)
+#define TX_PWR_CFG_3_STBC2_CH1         FIELD32(0xf0000000)
 
 /*
  * TX_PWR_CFG_4:
 #define TX_PWR_CFG_4_UKNOWN6           FIELD32(0x000000f0)
 #define TX_PWR_CFG_4_UKNOWN7           FIELD32(0x00000f00)
 #define TX_PWR_CFG_4_UKNOWN8           FIELD32(0x0000f000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_STBC4_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_STBC4_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_STBC6_CH0         FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_STBC6_CH1         FIELD32(0x0000f000)
 
 /*
  * TX_PIN_CFG:
  */
 #define EXP_ACK_TIME                   0x1380
 
+/* TX_PWR_CFG_5 */
+#define TX_PWR_CFG_5                   0x1384
+#define TX_PWR_CFG_5_MCS16_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_5_MCS16_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_5_MCS16_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_5_MCS18_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_5_MCS18_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_5_MCS18_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_6 */
+#define TX_PWR_CFG_6                   0x1388
+#define TX_PWR_CFG_6_MCS20_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_6_MCS20_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_6_MCS20_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_6_MCS22_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_6_MCS22_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_6_MCS22_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_0_EXT */
+#define TX_PWR_CFG_0_EXT               0x1390
+#define TX_PWR_CFG_0_EXT_CCK1_CH2      FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_EXT_CCK5_CH2      FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_EXT_OFDM6_CH2     FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_EXT_OFDM12_CH2    FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_1_EXT */
+#define TX_PWR_CFG_1_EXT               0x1394
+#define TX_PWR_CFG_1_EXT_OFDM24_CH2    FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_EXT_OFDM48_CH2    FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_EXT_MCS0_CH2      FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_EXT_MCS2_CH2      FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_2_EXT */
+#define TX_PWR_CFG_2_EXT               0x1398
+#define TX_PWR_CFG_2_EXT_MCS4_CH2      FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_EXT_MCS6_CH2      FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_EXT_MCS8_CH2      FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_EXT_MCS10_CH2     FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_3_EXT */
+#define TX_PWR_CFG_3_EXT               0x139c
+#define TX_PWR_CFG_3_EXT_MCS12_CH2     FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_EXT_MCS14_CH2     FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_EXT_STBC0_CH2     FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_EXT_STBC2_CH2     FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_4_EXT */
+#define TX_PWR_CFG_4_EXT               0x13a0
+#define TX_PWR_CFG_4_EXT_STBC4_CH2     FIELD32(0x0000000f)
+#define TX_PWR_CFG_4_EXT_STBC6_CH2     FIELD32(0x00000f00)
+
+/* TX_PWR_CFG_7 */
+#define TX_PWR_CFG_7                   0x13d4
+#define TX_PWR_CFG_7_OFDM54_CH0                FIELD32(0x0000000f)
+#define TX_PWR_CFG_7_OFDM54_CH1                FIELD32(0x000000f0)
+#define TX_PWR_CFG_7_OFDM54_CH2                FIELD32(0x00000f00)
+#define TX_PWR_CFG_7_MCS7_CH0          FIELD32(0x000f0000)
+#define TX_PWR_CFG_7_MCS7_CH1          FIELD32(0x00f00000)
+#define TX_PWR_CFG_7_MCS7_CH2          FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_8 */
+#define TX_PWR_CFG_8                   0x13d8
+#define TX_PWR_CFG_8_MCS15_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_8_MCS15_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_8_MCS15_CH2         FIELD32(0x00000f00)
+#define TX_PWR_CFG_8_MCS23_CH0         FIELD32(0x000f0000)
+#define TX_PWR_CFG_8_MCS23_CH1         FIELD32(0x00f00000)
+#define TX_PWR_CFG_8_MCS23_CH2         FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_9 */
+#define TX_PWR_CFG_9                   0x13dc
+#define TX_PWR_CFG_9_STBC7_CH0         FIELD32(0x0000000f)
+#define TX_PWR_CFG_9_STBC7_CH1         FIELD32(0x000000f0)
+#define TX_PWR_CFG_9_STBC7_CH2         FIELD32(0x00000f00)
+
 /*
  * RX_FILTER_CFG: RX configuration register.
  */
@@ -1902,11 +2019,13 @@ struct mac_iveiv_entry {
 #define HW_BEACON_BASE6                        0x5dc0
 #define HW_BEACON_BASE7                        0x5bc0
 
-#define HW_BEACON_OFFSET(__index) \
+#define HW_BEACON_BASE(__index) \
        (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \
          (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \
          (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))))
 
+#define BEACON_BASE_TO_OFFSET(_base)   (((_base) - 0x4000) / 64)
+
 /*
  * BBP registers.
  * The wordsize of the BBP is 8 bits.
@@ -1975,6 +2094,10 @@ struct mac_iveiv_entry {
 #define BBP109_TX0_POWER               FIELD8(0x0f)
 #define BBP109_TX1_POWER               FIELD8(0xf0)
 
+/* BBP 110 */
+#define BBP110_TX2_POWER               FIELD8(0x0f)
+
+
 /*
  * BBP 138: Unknown
  */
@@ -2024,6 +2147,12 @@ struct mac_iveiv_entry {
 #define RFCSR3_PA2_CASCODE_BIAS_CCKK   FIELD8(0x80)
 /* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */
 #define RFCSR3_VCOCAL_EN               FIELD8(0x80)
+/* Bits for RF3050 */
+#define RFCSR3_BIT1                    FIELD8(0x02)
+#define RFCSR3_BIT2                    FIELD8(0x04)
+#define RFCSR3_BIT3                    FIELD8(0x08)
+#define RFCSR3_BIT4                    FIELD8(0x10)
+#define RFCSR3_BIT5                    FIELD8(0x20)
 
 /*
  * FRCSR 5:
@@ -2036,6 +2165,8 @@ struct mac_iveiv_entry {
 #define RFCSR6_R1                      FIELD8(0x03)
 #define RFCSR6_R2                      FIELD8(0x40)
 #define RFCSR6_TXDIV           FIELD8(0x0c)
+/* bits for RF3053 */
+#define RFCSR6_VCO_IC                  FIELD8(0xc0)
 
 /*
  * RFCSR 7:
@@ -2060,7 +2191,12 @@ struct mac_iveiv_entry {
  * RFCSR 11:
  */
 #define RFCSR11_R                      FIELD8(0x03)
+#define RFCSR11_PLL_MOD                        FIELD8(0x0c)
 #define RFCSR11_MOD                    FIELD8(0xc0)
+/* bits for RF3053 */
+/* TODO: verify RFCSR11_MOD usage on other chips */
+#define RFCSR11_PLL_IDOH               FIELD8(0x40)
+
 
 /*
  * RFCSR 12:
@@ -2092,6 +2228,10 @@ struct mac_iveiv_entry {
 #define RFCSR17_R                      FIELD8(0x20)
 #define RFCSR17_CODE                   FIELD8(0x7f)
 
+/* RFCSR 18 */
+#define RFCSR18_XO_TUNE_BYPASS         FIELD8(0x40)
+
+
 /*
  * RFCSR 20:
  */
@@ -2152,6 +2292,12 @@ struct mac_iveiv_entry {
 #define RFCSR31_RX_H20M                        FIELD8(0x20)
 #define RFCSR31_RX_CALIB               FIELD8(0x7f)
 
+/* RFCSR 32 bits for RF3053 */
+#define RFCSR32_TX_AGC_FC              FIELD8(0xf8)
+
+/* RFCSR 36 bits for RF3053 */
+#define RFCSR36_RF_BS                  FIELD8(0x80)
+
 /*
  * RFCSR 38:
  */
@@ -2160,6 +2306,7 @@ struct mac_iveiv_entry {
 /*
  * RFCSR 39:
  */
+#define RFCSR39_RX_DIV                 FIELD8(0x40)
 #define RFCSR39_RX_LO2_EN              FIELD8(0x80)
 
 /*
@@ -2167,12 +2314,36 @@ struct mac_iveiv_entry {
  */
 #define RFCSR49_TX                     FIELD8(0x3f)
 #define RFCSR49_EP                     FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR49_TX_LO1_IC              FIELD8(0x1c)
+#define RFCSR49_TX_DIV                 FIELD8(0x20)
 
 /*
  * RFCSR 50:
  */
 #define RFCSR50_TX                     FIELD8(0x3f)
 #define RFCSR50_EP                     FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR50_TX_LO1_EN              FIELD8(0x20)
+#define RFCSR50_TX_LO2_EN              FIELD8(0x10)
+
+/* RFCSR 51 */
+/* bits for RT3593 */
+#define RFCSR51_BITS01                 FIELD8(0x03)
+#define RFCSR51_BITS24                 FIELD8(0x1c)
+#define RFCSR51_BITS57                 FIELD8(0xe0)
+
+#define RFCSR53_TX_POWER               FIELD8(0x3f)
+#define RFCSR53_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR54_TX_POWER               FIELD8(0x3f)
+#define RFCSR54_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR55_TX_POWER               FIELD8(0x3f)
+#define RFCSR55_UNKNOWN                        FIELD8(0xc0)
+
+#define RFCSR57_DRV_CC                 FIELD8(0xfc)
+
 
 /*
  * RF registers
@@ -2206,28 +2377,67 @@ struct mac_iveiv_entry {
  * The wordsize of the EEPROM is 16 bits.
  */
 
-/*
- * Chip ID
- */
-#define EEPROM_CHIP_ID                 0x0000
+enum rt2800_eeprom_word {
+       EEPROM_CHIP_ID = 0,
+       EEPROM_VERSION,
+       EEPROM_MAC_ADDR_0,
+       EEPROM_MAC_ADDR_1,
+       EEPROM_MAC_ADDR_2,
+       EEPROM_NIC_CONF0,
+       EEPROM_NIC_CONF1,
+       EEPROM_FREQ,
+       EEPROM_LED_AG_CONF,
+       EEPROM_LED_ACT_CONF,
+       EEPROM_LED_POLARITY,
+       EEPROM_NIC_CONF2,
+       EEPROM_LNA,
+       EEPROM_RSSI_BG,
+       EEPROM_RSSI_BG2,
+       EEPROM_TXMIXER_GAIN_BG,
+       EEPROM_RSSI_A,
+       EEPROM_RSSI_A2,
+       EEPROM_TXMIXER_GAIN_A,
+       EEPROM_EIRP_MAX_TX_POWER,
+       EEPROM_TXPOWER_DELTA,
+       EEPROM_TXPOWER_BG1,
+       EEPROM_TXPOWER_BG2,
+       EEPROM_TSSI_BOUND_BG1,
+       EEPROM_TSSI_BOUND_BG2,
+       EEPROM_TSSI_BOUND_BG3,
+       EEPROM_TSSI_BOUND_BG4,
+       EEPROM_TSSI_BOUND_BG5,
+       EEPROM_TXPOWER_A1,
+       EEPROM_TXPOWER_A2,
+       EEPROM_TSSI_BOUND_A1,
+       EEPROM_TSSI_BOUND_A2,
+       EEPROM_TSSI_BOUND_A3,
+       EEPROM_TSSI_BOUND_A4,
+       EEPROM_TSSI_BOUND_A5,
+       EEPROM_TXPOWER_BYRATE,
+       EEPROM_BBP_START,
+
+       /* IDs for extended EEPROM format used by three-chain devices */
+       EEPROM_EXT_LNA2,
+       EEPROM_EXT_TXPOWER_BG3,
+       EEPROM_EXT_TXPOWER_A3,
+
+       /* New values must be added before this */
+       EEPROM_WORD_COUNT
+};
 
 /*
  * EEPROM Version
  */
-#define EEPROM_VERSION                 0x0001
 #define EEPROM_VERSION_FAE             FIELD16(0x00ff)
 #define EEPROM_VERSION_VERSION         FIELD16(0xff00)
 
 /*
  * HW MAC address.
  */
-#define EEPROM_MAC_ADDR_0              0x0002
 #define EEPROM_MAC_ADDR_BYTE0          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE1          FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_1              0x0003
 #define EEPROM_MAC_ADDR_BYTE2          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE3          FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_2              0x0004
 #define EEPROM_MAC_ADDR_BYTE4          FIELD16(0x00ff)
 #define EEPROM_MAC_ADDR_BYTE5          FIELD16(0xff00)
 
@@ -2237,7 +2447,6 @@ struct mac_iveiv_entry {
  * TXPATH: 1: 1T, 2: 2T, 3: 3T
  * RF_TYPE: RFIC type
  */
-#define        EEPROM_NIC_CONF0                0x001a
 #define EEPROM_NIC_CONF0_RXPATH                FIELD16(0x000f)
 #define EEPROM_NIC_CONF0_TXPATH                FIELD16(0x00f0)
 #define EEPROM_NIC_CONF0_RF_TYPE               FIELD16(0x0f00)
@@ -2261,7 +2470,6 @@ struct mac_iveiv_entry {
  * BT_COEXIST: 0: disable, 1: enable
  * DAC_TEST: 0: disable, 1: enable
  */
-#define        EEPROM_NIC_CONF1                0x001b
 #define EEPROM_NIC_CONF1_HW_RADIO              FIELD16(0x0001)
 #define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC               FIELD16(0x0002)
 #define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G               FIELD16(0x0004)
@@ -2281,7 +2489,6 @@ struct mac_iveiv_entry {
 /*
  * EEPROM frequency
  */
-#define        EEPROM_FREQ                     0x001d
 #define EEPROM_FREQ_OFFSET             FIELD16(0x00ff)
 #define EEPROM_FREQ_LED_MODE           FIELD16(0x7f00)
 #define EEPROM_FREQ_LED_POLARITY       FIELD16(0x1000)
@@ -2298,9 +2505,6 @@ struct mac_iveiv_entry {
  * POLARITY_GPIO_4: Polarity GPIO4 setting.
  * LED_MODE: Led mode.
  */
-#define EEPROM_LED_AG_CONF             0x001e
-#define EEPROM_LED_ACT_CONF            0x001f
-#define EEPROM_LED_POLARITY            0x0020
 #define EEPROM_LED_POLARITY_RDY_BG     FIELD16(0x0001)
 #define EEPROM_LED_POLARITY_RDY_A      FIELD16(0x0002)
 #define EEPROM_LED_POLARITY_ACT                FIELD16(0x0004)
@@ -2317,7 +2521,6 @@ struct mac_iveiv_entry {
  * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
  * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
  */
-#define EEPROM_NIC_CONF2               0x0021
 #define EEPROM_NIC_CONF2_RX_STREAM             FIELD16(0x000f)
 #define EEPROM_NIC_CONF2_TX_STREAM             FIELD16(0x00f0)
 #define EEPROM_NIC_CONF2_CRYSTAL               FIELD16(0x0600)
@@ -2325,54 +2528,46 @@ struct mac_iveiv_entry {
 /*
  * EEPROM LNA
  */
-#define EEPROM_LNA                     0x0022
 #define EEPROM_LNA_BG                  FIELD16(0x00ff)
 #define EEPROM_LNA_A0                  FIELD16(0xff00)
 
 /*
  * EEPROM RSSI BG offset
  */
-#define EEPROM_RSSI_BG                 0x0023
 #define EEPROM_RSSI_BG_OFFSET0         FIELD16(0x00ff)
 #define EEPROM_RSSI_BG_OFFSET1         FIELD16(0xff00)
 
 /*
  * EEPROM RSSI BG2 offset
  */
-#define EEPROM_RSSI_BG2                        0x0024
 #define EEPROM_RSSI_BG2_OFFSET2                FIELD16(0x00ff)
 #define EEPROM_RSSI_BG2_LNA_A1         FIELD16(0xff00)
 
 /*
  * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2).
  */
-#define EEPROM_TXMIXER_GAIN_BG         0x0024
 #define EEPROM_TXMIXER_GAIN_BG_VAL     FIELD16(0x0007)
 
 /*
  * EEPROM RSSI A offset
  */
-#define EEPROM_RSSI_A                  0x0025
 #define EEPROM_RSSI_A_OFFSET0          FIELD16(0x00ff)
 #define EEPROM_RSSI_A_OFFSET1          FIELD16(0xff00)
 
 /*
  * EEPROM RSSI A2 offset
  */
-#define EEPROM_RSSI_A2                 0x0026
 #define EEPROM_RSSI_A2_OFFSET2         FIELD16(0x00ff)
 #define EEPROM_RSSI_A2_LNA_A2          FIELD16(0xff00)
 
 /*
  * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2).
  */
-#define EEPROM_TXMIXER_GAIN_A          0x0026
 #define EEPROM_TXMIXER_GAIN_A_VAL      FIELD16(0x0007)
 
 /*
  * EEPROM EIRP Maximum TX power values(unit: dbm)
  */
-#define EEPROM_EIRP_MAX_TX_POWER       0x0027
 #define EEPROM_EIRP_MAX_TX_POWER_2GHZ  FIELD16(0x00ff)
 #define EEPROM_EIRP_MAX_TX_POWER_5GHZ  FIELD16(0xff00)
 
@@ -2383,7 +2578,6 @@ struct mac_iveiv_entry {
  * TYPE: 1: Plus the delta value, 0: minus the delta value
  * ENABLE: enable tx power compensation for 40BW
  */
-#define EEPROM_TXPOWER_DELTA           0x0028
 #define EEPROM_TXPOWER_DELTA_VALUE_2G  FIELD16(0x003f)
 #define EEPROM_TXPOWER_DELTA_TYPE_2G   FIELD16(0x0040)
 #define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080)
@@ -2394,8 +2588,6 @@ struct mac_iveiv_entry {
 /*
  * EEPROM TXPOWER 802.11BG
  */
-#define        EEPROM_TXPOWER_BG1              0x0029
-#define        EEPROM_TXPOWER_BG2              0x0030
 #define EEPROM_TXPOWER_BG_SIZE         7
 #define EEPROM_TXPOWER_BG_1            FIELD16(0x00ff)
 #define EEPROM_TXPOWER_BG_2            FIELD16(0xff00)
@@ -2407,7 +2599,6 @@ struct mac_iveiv_entry {
  * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -3)
  */
-#define EEPROM_TSSI_BOUND_BG1          0x0037
 #define EEPROM_TSSI_BOUND_BG1_MINUS4   FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG1_MINUS3   FIELD16(0xff00)
 
@@ -2418,7 +2609,6 @@ struct mac_iveiv_entry {
  * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -1)
  */
-#define EEPROM_TSSI_BOUND_BG2          0x0038
 #define EEPROM_TSSI_BOUND_BG2_MINUS2   FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG2_MINUS1   FIELD16(0xff00)
 
@@ -2428,7 +2618,6 @@ struct mac_iveiv_entry {
  * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 1)
  */
-#define EEPROM_TSSI_BOUND_BG3          0x0039
 #define EEPROM_TSSI_BOUND_BG3_REF      FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG3_PLUS1    FIELD16(0xff00)
 
@@ -2439,7 +2628,6 @@ struct mac_iveiv_entry {
  * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 3)
  */
-#define EEPROM_TSSI_BOUND_BG4          0x003a
 #define EEPROM_TSSI_BOUND_BG4_PLUS2    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG4_PLUS3    FIELD16(0xff00)
 
@@ -2449,19 +2637,20 @@ struct mac_iveiv_entry {
  *        increased by (agc_step * 4)
  * AGC_STEP: Temperature compensation step.
  */
-#define EEPROM_TSSI_BOUND_BG5          0x003b
 #define EEPROM_TSSI_BOUND_BG5_PLUS4    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00)
 
 /*
  * EEPROM TXPOWER 802.11A
  */
-#define EEPROM_TXPOWER_A1              0x003c
-#define EEPROM_TXPOWER_A2              0x0053
 #define EEPROM_TXPOWER_A_SIZE          6
 #define EEPROM_TXPOWER_A_1             FIELD16(0x00ff)
 #define EEPROM_TXPOWER_A_2             FIELD16(0xff00)
 
+/* EEPROM_TXPOWER_{A,G} fields for RT3593 */
+#define EEPROM_TXPOWER_ALC             FIELD8(0x1f)
+#define EEPROM_TXPOWER_FINE_CTRL       FIELD8(0xe0)
+
 /*
  * EEPROM temperature compensation boundaries 802.11A
  * MINUS4: If the actual TSSI is below this boundary, tx power needs to be
@@ -2469,7 +2658,6 @@ struct mac_iveiv_entry {
  * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -3)
  */
-#define EEPROM_TSSI_BOUND_A1           0x006a
 #define EEPROM_TSSI_BOUND_A1_MINUS4    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A1_MINUS3    FIELD16(0xff00)
 
@@ -2480,7 +2668,6 @@ struct mac_iveiv_entry {
  * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
  *         reduced by (agc_step * -1)
  */
-#define EEPROM_TSSI_BOUND_A2           0x006b
 #define EEPROM_TSSI_BOUND_A2_MINUS2    FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A2_MINUS1    FIELD16(0xff00)
 
@@ -2490,7 +2677,6 @@ struct mac_iveiv_entry {
  * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 1)
  */
-#define EEPROM_TSSI_BOUND_A3           0x006c
 #define EEPROM_TSSI_BOUND_A3_REF       FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A3_PLUS1     FIELD16(0xff00)
 
@@ -2501,7 +2687,6 @@ struct mac_iveiv_entry {
  * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
  *        increased by (agc_step * 3)
  */
-#define EEPROM_TSSI_BOUND_A4           0x006d
 #define EEPROM_TSSI_BOUND_A4_PLUS2     FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A4_PLUS3     FIELD16(0xff00)
 
@@ -2511,14 +2696,12 @@ struct mac_iveiv_entry {
  *        increased by (agc_step * 4)
  * AGC_STEP: Temperature compensation step.
  */
-#define EEPROM_TSSI_BOUND_A5           0x006e
 #define EEPROM_TSSI_BOUND_A5_PLUS4     FIELD16(0x00ff)
 #define EEPROM_TSSI_BOUND_A5_AGC_STEP  FIELD16(0xff00)
 
 /*
  * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode
  */
-#define EEPROM_TXPOWER_BYRATE          0x006f
 #define EEPROM_TXPOWER_BYRATE_SIZE     9
 
 #define EEPROM_TXPOWER_BYRATE_RATE0    FIELD16(0x000f)
@@ -2529,11 +2712,14 @@ struct mac_iveiv_entry {
 /*
  * EEPROM BBP.
  */
-#define        EEPROM_BBP_START                0x0078
 #define EEPROM_BBP_SIZE                        16
 #define EEPROM_BBP_VALUE               FIELD16(0x00ff)
 #define EEPROM_BBP_REG_ID              FIELD16(0xff00)
 
+/* EEPROM_EXT_LNA2 */
+#define EEPROM_EXT_LNA2_A1             FIELD16(0x00ff)
+#define EEPROM_EXT_LNA2_A2             FIELD16(0xff00)
+
 /*
  * EEPROM IQ Calibration, unlike other entries those are byte addresses.
  */
@@ -2610,6 +2796,7 @@ struct mac_iveiv_entry {
 #define MCU_RADAR                      0x60
 #define MCU_BOOT_SIGNAL                        0x72
 #define MCU_ANT_SELECT                 0X73
+#define MCU_FREQ_OFFSET                        0x74
 #define MCU_BBP_SIGNAL                 0x80
 #define MCU_POWER_SAVE                 0x83
 #define MCU_BAND_SELECT                0x91
@@ -2630,6 +2817,7 @@ struct mac_iveiv_entry {
 #define TXWI_DESC_SIZE_5WORDS          (5 * sizeof(__le32))
 
 #define RXWI_DESC_SIZE_4WORDS          (4 * sizeof(__le32))
+#define RXWI_DESC_SIZE_5WORDS          (5 * sizeof(__le32))
 #define RXWI_DESC_SIZE_6WORDS          (6 * sizeof(__le32))
 
 /*
@@ -2750,18 +2938,15 @@ struct mac_iveiv_entry {
 #define MAX_A_TXPOWER  15
 #define DEFAULT_TXPOWER        5
 
+#define MIN_A_TXPOWER_3593     0
+#define MAX_A_TXPOWER_3593     31
+
 #define TXPOWER_G_FROM_DEV(__txpower) \
        ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
 
-#define TXPOWER_G_TO_DEV(__txpower) \
-       clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER)
-
 #define TXPOWER_A_FROM_DEV(__txpower) \
        ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
 
-#define TXPOWER_A_TO_DEV(__txpower) \
-       clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER)
-
 /*
  *  Board's maximun TX power limitation
  */
index 1b41c8e..95e6e61 100644 (file)
@@ -221,6 +221,157 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
        mutex_unlock(&rt2x00dev->csr_mutex);
 }
 
+static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
+       [EEPROM_CHIP_ID]                = 0x0000,
+       [EEPROM_VERSION]                = 0x0001,
+       [EEPROM_MAC_ADDR_0]             = 0x0002,
+       [EEPROM_MAC_ADDR_1]             = 0x0003,
+       [EEPROM_MAC_ADDR_2]             = 0x0004,
+       [EEPROM_NIC_CONF0]              = 0x001a,
+       [EEPROM_NIC_CONF1]              = 0x001b,
+       [EEPROM_FREQ]                   = 0x001d,
+       [EEPROM_LED_AG_CONF]            = 0x001e,
+       [EEPROM_LED_ACT_CONF]           = 0x001f,
+       [EEPROM_LED_POLARITY]           = 0x0020,
+       [EEPROM_NIC_CONF2]              = 0x0021,
+       [EEPROM_LNA]                    = 0x0022,
+       [EEPROM_RSSI_BG]                = 0x0023,
+       [EEPROM_RSSI_BG2]               = 0x0024,
+       [EEPROM_TXMIXER_GAIN_BG]        = 0x0024, /* overlaps with RSSI_BG2 */
+       [EEPROM_RSSI_A]                 = 0x0025,
+       [EEPROM_RSSI_A2]                = 0x0026,
+       [EEPROM_TXMIXER_GAIN_A]         = 0x0026, /* overlaps with RSSI_A2 */
+       [EEPROM_EIRP_MAX_TX_POWER]      = 0x0027,
+       [EEPROM_TXPOWER_DELTA]          = 0x0028,
+       [EEPROM_TXPOWER_BG1]            = 0x0029,
+       [EEPROM_TXPOWER_BG2]            = 0x0030,
+       [EEPROM_TSSI_BOUND_BG1]         = 0x0037,
+       [EEPROM_TSSI_BOUND_BG2]         = 0x0038,
+       [EEPROM_TSSI_BOUND_BG3]         = 0x0039,
+       [EEPROM_TSSI_BOUND_BG4]         = 0x003a,
+       [EEPROM_TSSI_BOUND_BG5]         = 0x003b,
+       [EEPROM_TXPOWER_A1]             = 0x003c,
+       [EEPROM_TXPOWER_A2]             = 0x0053,
+       [EEPROM_TSSI_BOUND_A1]          = 0x006a,
+       [EEPROM_TSSI_BOUND_A2]          = 0x006b,
+       [EEPROM_TSSI_BOUND_A3]          = 0x006c,
+       [EEPROM_TSSI_BOUND_A4]          = 0x006d,
+       [EEPROM_TSSI_BOUND_A5]          = 0x006e,
+       [EEPROM_TXPOWER_BYRATE]         = 0x006f,
+       [EEPROM_BBP_START]              = 0x0078,
+};
+
+static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
+       [EEPROM_CHIP_ID]                = 0x0000,
+       [EEPROM_VERSION]                = 0x0001,
+       [EEPROM_MAC_ADDR_0]             = 0x0002,
+       [EEPROM_MAC_ADDR_1]             = 0x0003,
+       [EEPROM_MAC_ADDR_2]             = 0x0004,
+       [EEPROM_NIC_CONF0]              = 0x001a,
+       [EEPROM_NIC_CONF1]              = 0x001b,
+       [EEPROM_NIC_CONF2]              = 0x001c,
+       [EEPROM_EIRP_MAX_TX_POWER]      = 0x0020,
+       [EEPROM_FREQ]                   = 0x0022,
+       [EEPROM_LED_AG_CONF]            = 0x0023,
+       [EEPROM_LED_ACT_CONF]           = 0x0024,
+       [EEPROM_LED_POLARITY]           = 0x0025,
+       [EEPROM_LNA]                    = 0x0026,
+       [EEPROM_EXT_LNA2]               = 0x0027,
+       [EEPROM_RSSI_BG]                = 0x0028,
+       [EEPROM_TXPOWER_DELTA]          = 0x0028, /* Overlaps with RSSI_BG */
+       [EEPROM_RSSI_BG2]               = 0x0029,
+       [EEPROM_TXMIXER_GAIN_BG]        = 0x0029, /* Overlaps with RSSI_BG2 */
+       [EEPROM_RSSI_A]                 = 0x002a,
+       [EEPROM_RSSI_A2]                = 0x002b,
+       [EEPROM_TXMIXER_GAIN_A]         = 0x002b, /* Overlaps with RSSI_A2 */
+       [EEPROM_TXPOWER_BG1]            = 0x0030,
+       [EEPROM_TXPOWER_BG2]            = 0x0037,
+       [EEPROM_EXT_TXPOWER_BG3]        = 0x003e,
+       [EEPROM_TSSI_BOUND_BG1]         = 0x0045,
+       [EEPROM_TSSI_BOUND_BG2]         = 0x0046,
+       [EEPROM_TSSI_BOUND_BG3]         = 0x0047,
+       [EEPROM_TSSI_BOUND_BG4]         = 0x0048,
+       [EEPROM_TSSI_BOUND_BG5]         = 0x0049,
+       [EEPROM_TXPOWER_A1]             = 0x004b,
+       [EEPROM_TXPOWER_A2]             = 0x0065,
+       [EEPROM_EXT_TXPOWER_A3]         = 0x007f,
+       [EEPROM_TSSI_BOUND_A1]          = 0x009a,
+       [EEPROM_TSSI_BOUND_A2]          = 0x009b,
+       [EEPROM_TSSI_BOUND_A3]          = 0x009c,
+       [EEPROM_TSSI_BOUND_A4]          = 0x009d,
+       [EEPROM_TSSI_BOUND_A5]          = 0x009e,
+       [EEPROM_TXPOWER_BYRATE]         = 0x00a0,
+};
+
+static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev,
+                                            const enum rt2800_eeprom_word word)
+{
+       const unsigned int *map;
+       unsigned int index;
+
+       if (WARN_ONCE(word >= EEPROM_WORD_COUNT,
+                     "%s: invalid EEPROM word %d\n",
+                     wiphy_name(rt2x00dev->hw->wiphy), word))
+               return 0;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               map = rt2800_eeprom_map_ext;
+       else
+               map = rt2800_eeprom_map;
+
+       index = map[word];
+
+       /* Index 0 is valid only for EEPROM_CHIP_ID.
+        * Otherwise it means that the offset of the
+        * given word is not initialized in the map,
+        * or that the field is not usable on the
+        * actual chipset.
+        */
+       WARN_ONCE(word != EEPROM_CHIP_ID && index == 0,
+                 "%s: invalid access of EEPROM word %d\n",
+                 wiphy_name(rt2x00dev->hw->wiphy), word);
+
+       return index;
+}
+
+static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev,
+                               const enum rt2800_eeprom_word word)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       return rt2x00_eeprom_addr(rt2x00dev, index);
+}
+
+static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev,
+                              const enum rt2800_eeprom_word word, u16 *data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       rt2x00_eeprom_read(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev,
+                               const enum rt2800_eeprom_word word, u16 data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, word);
+       rt2x00_eeprom_write(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev,
+                                         const enum rt2800_eeprom_word array,
+                                         unsigned int offset,
+                                         u16 *data)
+{
+       unsigned int index;
+
+       index = rt2800_eeprom_word_index(rt2x00dev, array);
+       rt2x00_eeprom_read(rt2x00dev, index + offset, data);
+}
+
 static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
@@ -370,6 +521,29 @@ void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2800_disable_wpdma);
 
+void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev,
+                              unsigned short *txwi_size,
+                              unsigned short *rxwi_size)
+{
+       switch (rt2x00dev->chip.rt) {
+       case RT3593:
+               *txwi_size = TXWI_DESC_SIZE_4WORDS;
+               *rxwi_size = RXWI_DESC_SIZE_5WORDS;
+               break;
+
+       case RT5592:
+               *txwi_size = TXWI_DESC_SIZE_5WORDS;
+               *rxwi_size = RXWI_DESC_SIZE_6WORDS;
+               break;
+
+       default:
+               *txwi_size = TXWI_DESC_SIZE_4WORDS;
+               *rxwi_size = RXWI_DESC_SIZE_4WORDS;
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size);
+
 static bool rt2800_check_firmware_crc(const u8 *data, const size_t len)
 {
        u16 fw_crc;
@@ -609,16 +783,16 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2)
        u8 offset2;
 
        if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
                offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
                offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
                offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2);
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
                offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0);
                offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
                offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2);
        }
 
@@ -766,6 +940,18 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi)
 }
 EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
 
+static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
+                                         unsigned int index)
+{
+       return HW_BEACON_BASE(index);
+}
+
+static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
+                                         unsigned int index)
+{
+       return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -818,7 +1004,8 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
                return;
        }
 
-       beacon_base = HW_BEACON_OFFSET(entry->entry_idx);
+       beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx);
+
        rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
                                   entry->skb->len + padding_len);
 
@@ -837,10 +1024,13 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 EXPORT_SYMBOL_GPL(rt2800_write_beacon);
 
 static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
-                                               unsigned int beacon_base)
+                                               unsigned int index)
 {
        int i;
        const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
+       unsigned int beacon_base;
+
+       beacon_base = rt2800_hw_beacon_base(rt2x00dev, index);
 
        /*
         * For the Beacon base registers we only need to clear
@@ -867,8 +1057,7 @@ void rt2800_clear_beacon(struct queue_entry *entry)
        /*
         * Clear beacon.
         */
-       rt2800_clear_beacon_register(rt2x00dev,
-                                    HW_BEACON_OFFSET(entry->entry_idx));
+       rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
 
        /*
         * Enabled beaconing again.
@@ -890,6 +1079,9 @@ const struct rt2x00debug rt2800_rt2x00debug = {
                .word_count     = CSR_REG_SIZE / sizeof(u32),
        },
        .eeprom = {
+               /* NOTE: The local EEPROM access functions can't
+                * be used here, use the generic versions instead.
+                */
                .read           = rt2x00_eeprom_read,
                .write          = rt2x00_eeprom_write,
                .word_base      = EEPROM_BASE,
@@ -1547,7 +1739,7 @@ static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev)
        led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3;
        if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) ||
            led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
                led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE);
                if (led_ctrl == 0 || led_ctrl > 0x40) {
                        rt2x00_set_field32(&reg, LED_CFG_G_LED_MODE, led_g_mode);
@@ -1609,7 +1801,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                        rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
                break;
        case 3:
-               rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
+               rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
                break;
        }
 
@@ -1622,7 +1814,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
                    rt2x00_rt(rt2x00dev, RT3090) ||
                    rt2x00_rt(rt2x00dev, RT3352) ||
                    rt2x00_rt(rt2x00dev, RT3390)) {
-                       rt2x00_eeprom_read(rt2x00dev,
+                       rt2800_eeprom_read(rt2x00dev,
                                           EEPROM_NIC_CONF1, &eeprom);
                        if (rt2x00_get_field16(eeprom,
                                                EEPROM_NIC_CONF1_ANT_DIVERSITY))
@@ -1649,6 +1841,13 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
 
        rt2800_bbp_write(rt2x00dev, 3, r3);
        rt2800_bbp_write(rt2x00dev, 1, r1);
+
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (ant->rx_chain_num == 1)
+                       rt2800_bbp_write(rt2x00dev, 86, 0x00);
+               else
+                       rt2800_bbp_write(rt2x00dev, 86, 0x46);
+       }
 }
 EXPORT_SYMBOL_GPL(rt2800_config_ant);
 
@@ -1659,22 +1858,73 @@ static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev,
        short lna_gain;
 
        if (libconf->rf.channel <= 14) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
                lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG);
        } else if (libconf->rf.channel <= 64) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
                lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0);
        } else if (libconf->rf.channel <= 128) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
-               lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1);
+               if (rt2x00_rt(rt2x00dev, RT3593)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_EXT_LNA2_A1);
+               } else {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_RSSI_BG2_LNA_A1);
+               }
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
-               lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2);
+               if (rt2x00_rt(rt2x00dev, RT3593)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_EXT_LNA2_A2);
+               } else {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+                       lna_gain = rt2x00_get_field16(eeprom,
+                                                     EEPROM_RSSI_A2_LNA_A2);
+               }
        }
 
        rt2x00dev->lna_gain = lna_gain;
 }
 
+#define FREQ_OFFSET_BOUND      0x5f
+
+static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev)
+{
+       u8 freq_offset, prev_freq_offset;
+       u8 rfcsr, prev_rfcsr;
+
+       freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE);
+       freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND);
+
+       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
+       prev_rfcsr = rfcsr;
+
+       rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset);
+       if (rfcsr == prev_rfcsr)
+               return;
+
+       if (rt2x00_is_usb(rt2x00dev)) {
+               rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff,
+                                  freq_offset, prev_rfcsr);
+               return;
+       }
+
+       prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE);
+       while (prev_freq_offset != freq_offset) {
+               if (prev_freq_offset < freq_offset)
+                       prev_freq_offset++;
+               else
+                       prev_freq_offset--;
+
+               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset);
+               rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+
+               usleep_range(1000, 1500);
+       }
+}
+
 static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev,
                                         struct ieee80211_conf *conf,
                                         struct rf_channel *rf,
@@ -1993,22 +2243,306 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
        rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
 }
 
-#define POWER_BOUND            0x27
-#define POWER_BOUND_5G         0x2b
-#define FREQ_OFFSET_BOUND      0x5f
-
-static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev)
+static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_conf *conf,
+                                        struct rf_channel *rf,
+                                        struct channel_info *info)
 {
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u8 txrx_agc_fc;
+       u8 txrx_h20m;
        u8 rfcsr;
+       u8 bbp;
+       const bool txbf_enabled = false; /* TODO */
 
-       rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
-       if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
+       /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */
+       rt2800_bbp_read(rt2x00dev, 109, &bbp);
+       rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0);
+       rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0);
+       rt2800_bbp_write(rt2x00dev, 109, bbp);
+
+       rt2800_bbp_read(rt2x00dev, 110, &bbp);
+       rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0);
+       rt2800_bbp_write(rt2x00dev, 110, bbp);
+
+       if (rf->channel <= 14) {
+               /* Restore BBP 25 & 26 for 2.4 GHz */
+               rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25);
+               rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26);
+       } else {
+               /* Hard code BBP 25 & 26 for 5GHz */
+
+               /* Enable IQ Phase correction */
+               rt2800_bbp_write(rt2x00dev, 25, 0x09);
+               /* Setup IQ Phase correction value */
+               rt2800_bbp_write(rt2x00dev, 26, 0xff);
+       }
+
+       rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1);
+       rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf);
+
+       rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3));
+       rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1);
        else
-               rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
-       rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+               rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2);
+       rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+                                 info->default_power1 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+                                 ((info->default_power1 & 0x18) << 1) |
+                                 (info->default_power1 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 53, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+                                 info->default_power2 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+                                 ((info->default_power2 & 0x18) << 1) |
+                                 (info->default_power2 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 55, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr);
+       if (rf->channel <= 14) {
+               rfcsr = 0;
+               rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+                                 info->default_power3 & 0x1f);
+       } else {
+               if (rt2x00_is_usb(rt2x00dev))
+                       rfcsr = 0x40;
+
+               rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+                                 ((info->default_power3 & 0x18) << 1) |
+                                 (info->default_power3 & 7));
+       }
+       rt2800_rfcsr_write(rt2x00dev, 54, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+
+       switch (rt2x00dev->default_ant.tx_chain_num) {
+       case 3:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
+               /* fallthrough */
+       case 2:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+               /* fallthrough */
+       case 1:
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
+               break;
+       }
+
+       switch (rt2x00dev->default_ant.rx_chain_num) {
+       case 3:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
+               /* fallthrough */
+       case 2:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+               /* fallthrough */
+       case 1:
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
+               break;
+       }
+       rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+       rt2800_adjust_freq_offset(rt2x00dev);
+
+       if (conf_is_ht40(conf)) {
+               txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40,
+                                               RFCSR24_TX_AGC_FC);
+               txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40,
+                                             RFCSR24_TX_H20M);
+       } else {
+               txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20,
+                                               RFCSR24_TX_AGC_FC);
+               txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20,
+                                             RFCSR24_TX_H20M);
+       }
+
+       /* NOTE: the reference driver does not writes the new value
+        * back to RFCSR 32
+        */
+       rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc);
+
+       if (rf->channel <= 14)
+               rfcsr = 0xa0;
+       else
+               rfcsr = 0x80;
+       rt2800_rfcsr_write(rt2x00dev, 31, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       /* Band selection */
+       rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0);
+       rt2800_rfcsr_write(rt2x00dev, 36, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr);
+       if (rf->channel <= 14)
+               rfcsr = 0x3c;
+       else
+               rfcsr = 0x20;
+       rt2800_rfcsr_write(rt2x00dev, 34, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
+       if (rf->channel <= 14)
+               rfcsr = 0x1a;
+       else
+               rfcsr = 0x12;
+       rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+       if (rf->channel >= 1 && rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+       else if (rf->channel >= 36 && rf->channel <= 64)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+       else if (rf->channel >= 100 && rf->channel <= 128)
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+       rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+
+       if (rf->channel <= 14) {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+               rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+       } else {
+               rt2800_rfcsr_write(rt2x00dev, 10, 0xd8);
+               rt2800_rfcsr_write(rt2x00dev, 13, 0x23);
+       }
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1);
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       if (rf->channel <= 14) {
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5);
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3);
+       } else {
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4);
+               rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2);
+       }
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2);
+
+       if (txbf_enabled)
+               rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1);
+
+       rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr);
+       if (rf->channel <= 14)
+               rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b);
+       else
+               rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f);
+       rt2800_rfcsr_write(rt2x00dev, 57, rfcsr);
+
+       if (rf->channel <= 14) {
+               rt2800_rfcsr_write(rt2x00dev, 44, 0x93);
+               rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+       } else {
+               rt2800_rfcsr_write(rt2x00dev, 44, 0x9b);
+               rt2800_rfcsr_write(rt2x00dev, 52, 0x05);
+       }
+
+       /* Initiate VCO calibration */
+       rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
+       if (rf->channel <= 14) {
+               rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+       } else {
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+       }
+       rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
+
+       if (rf->channel >= 1 && rf->channel <= 14) {
+               rfcsr = 0x23;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+       } else if (rf->channel >= 36 && rf->channel <= 64) {
+               rfcsr = 0x36;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, 0x36);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xeb);
+       } else if (rf->channel >= 100 && rf->channel <= 128) {
+               rfcsr = 0x32;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0xb3);
+       } else {
+               rfcsr = 0x30;
+               if (txbf_enabled)
+                       rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+               rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+               rt2800_rfcsr_write(rt2x00dev, 45, 0x9b);
+       }
 }
 
+#define POWER_BOUND            0x27
+#define POWER_BOUND_5G         0x2b
+
 static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev,
                                         struct ieee80211_conf *conf,
                                         struct rf_channel *rf,
@@ -2563,6 +3097,23 @@ static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel)
        rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0);
 }
 
+static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev,
+                                 unsigned int channel,
+                                 char txpower)
+{
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC);
+
+       if (channel <= 14)
+               return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER);
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return clamp_t(char, txpower, MIN_A_TXPOWER_3593,
+                              MAX_A_TXPOWER_3593);
+       else
+               return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER);
+}
+
 static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                                  struct ieee80211_conf *conf,
                                  struct rf_channel *rf,
@@ -2572,13 +3123,14 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        unsigned int tx_pin;
        u8 bbp, rfcsr;
 
-       if (rf->channel <= 14) {
-               info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
-               info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
-       } else {
-               info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
-               info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
-       }
+       info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                                    info->default_power1);
+       info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                                    info->default_power2);
+       if (rt2x00dev->default_ant.tx_chain_num > 2)
+               info->default_power3 =
+                       rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+                                             info->default_power3);
 
        switch (rt2x00dev->chip.rf) {
        case RF2020:
@@ -2591,6 +3143,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        case RF3052:
                rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info);
                break;
+       case RF3053:
+               rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info);
+               break;
        case RF3290:
                rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info);
                break;
@@ -2636,6 +3191,23 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
                rt2800_bbp_write(rt2x00dev, 27, 0x20);
                rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
+       } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rf->channel > 14) {
+                       /* Disable CCK Packet detection on 5GHz */
+                       rt2800_bbp_write(rt2x00dev, 70, 0x00);
+               } else {
+                       rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+               }
+
+               if (conf_is_ht40(conf))
+                       rt2800_bbp_write(rt2x00dev, 105, 0x04);
+               else
+                       rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+               rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
+               rt2800_bbp_write(rt2x00dev, 77, 0x98);
        } else {
                rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
                rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
@@ -2651,16 +3223,27 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
                                rt2800_bbp_write(rt2x00dev, 82, 0x62);
                                rt2800_bbp_write(rt2x00dev, 75, 0x46);
                        } else {
-                               rt2800_bbp_write(rt2x00dev, 82, 0x84);
+                               if (rt2x00_rt(rt2x00dev, RT3593))
+                                       rt2800_bbp_write(rt2x00dev, 82, 0x62);
+                               else
+                                       rt2800_bbp_write(rt2x00dev, 82, 0x84);
                                rt2800_bbp_write(rt2x00dev, 75, 0x50);
                        }
+                       if (rt2x00_rt(rt2x00dev, RT3593))
+                               rt2800_bbp_write(rt2x00dev, 83, 0x8a);
                }
+
        } else {
                if (rt2x00_rt(rt2x00dev, RT3572))
                        rt2800_bbp_write(rt2x00dev, 82, 0x94);
+               else if (rt2x00_rt(rt2x00dev, RT3593))
+                       rt2800_bbp_write(rt2x00dev, 82, 0x82);
                else
                        rt2800_bbp_write(rt2x00dev, 82, 0xf2);
 
+               if (rt2x00_rt(rt2x00dev, RT3593))
+                       rt2800_bbp_write(rt2x00dev, 83, 0x9a);
+
                if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
                        rt2800_bbp_write(rt2x00dev, 75, 0x46);
                else
@@ -2731,6 +3314,41 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        if (rt2x00_rt(rt2x00dev, RT3572))
                rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
 
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_is_usb(rt2x00dev)) {
+                       rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+
+                       /* Band selection. GPIO #8 controls all paths */
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR8, 0);
+                       if (rf->channel <= 14)
+                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 1);
+                       else
+                               rt2x00_set_field32(&reg, GPIO_CTRL_VAL8, 0);
+
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR4, 0);
+                       rt2x00_set_field32(&reg, GPIO_CTRL_DIR7, 0);
+
+                       /* LNA PE control.
+                       * GPIO #4 controls PE0 and PE1,
+                       * GPIO #7 controls PE2
+                       */
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL4, 1);
+                       rt2x00_set_field32(&reg, GPIO_CTRL_VAL7, 1);
+
+                       rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
+               }
+
+               /* AGC init */
+               if (rf->channel <= 14)
+                       reg = 0x1c + 2 * rt2x00dev->lna_gain;
+               else
+                       reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3);
+
+               rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
+
+               usleep_range(1000, 1500);
+       }
+
        if (rt2x00_rt(rt2x00dev, RT5592)) {
                rt2800_bbp_write(rt2x00dev, 195, 141);
                rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a);
@@ -2790,6 +3408,13 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev)
        int i;
 
        /*
+        * First check if temperature compensation is supported.
+        */
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC))
+               return 0;
+
+       /*
         * Read TSSI boundaries for temperature compensation from
         * the EEPROM.
         *
@@ -2798,62 +3423,62 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev)
         * Example TSSI bounds  0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00
         */
        if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
                tssi_bounds[0] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG1_MINUS4);
                tssi_bounds[1] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG1_MINUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
                tssi_bounds[2] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG2_MINUS2);
                tssi_bounds[3] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG2_MINUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
                tssi_bounds[4] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG3_REF);
                tssi_bounds[5] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG3_PLUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
                tssi_bounds[6] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG4_PLUS2);
                tssi_bounds[7] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG4_PLUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
                tssi_bounds[8] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_BG5_PLUS4);
 
                step = rt2x00_get_field16(eeprom,
                                          EEPROM_TSSI_BOUND_BG5_AGC_STEP);
        } else {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
                tssi_bounds[0] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A1_MINUS4);
                tssi_bounds[1] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A1_MINUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
                tssi_bounds[2] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A2_MINUS2);
                tssi_bounds[3] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A2_MINUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
                tssi_bounds[4] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A3_REF);
                tssi_bounds[5] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A3_PLUS1);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
                tssi_bounds[6] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A4_PLUS2);
                tssi_bounds[7] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A4_PLUS3);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
                tssi_bounds[8] = rt2x00_get_field16(eeprom,
                                        EEPROM_TSSI_BOUND_A5_PLUS4);
 
@@ -2899,7 +3524,7 @@ static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev,
        u8 comp_type;
        int comp_value = 0;
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
 
        /*
         * HT40 compensation not required.
@@ -2966,6 +3591,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        u8 eirp_txpower_criterion;
        u8 reg_limit;
 
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return min_t(u8, txpower, 0xc);
+
        if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
                /*
                 * Check if eirp txpower exceed txpower_limit.
@@ -2974,12 +3602,12 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
                 * .11b data rate need add additional 4dbm
                 * when calculating eirp txpower.
                 */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + 1,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             1, &eeprom);
                criterion = rt2x00_get_field16(eeprom,
                                               EEPROM_TXPOWER_BYRATE_RATE0);
 
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
+               rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
                                   &eeprom);
 
                if (band == IEEE80211_BAND_2GHZ)
@@ -3001,6 +3629,412 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
        return min_t(u8, txpower, 0xc);
 }
 
+
+enum {
+       TX_PWR_CFG_0_IDX,
+       TX_PWR_CFG_1_IDX,
+       TX_PWR_CFG_2_IDX,
+       TX_PWR_CFG_3_IDX,
+       TX_PWR_CFG_4_IDX,
+       TX_PWR_CFG_5_IDX,
+       TX_PWR_CFG_6_IDX,
+       TX_PWR_CFG_7_IDX,
+       TX_PWR_CFG_8_IDX,
+       TX_PWR_CFG_9_IDX,
+       TX_PWR_CFG_0_EXT_IDX,
+       TX_PWR_CFG_1_EXT_IDX,
+       TX_PWR_CFG_2_EXT_IDX,
+       TX_PWR_CFG_3_EXT_IDX,
+       TX_PWR_CFG_4_EXT_IDX,
+       TX_PWR_CFG_IDX_COUNT,
+};
+
+static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_channel *chan,
+                                        int power_level)
+{
+       u8 txpower;
+       u16 eeprom;
+       u32 regs[TX_PWR_CFG_IDX_COUNT];
+       unsigned int offset;
+       enum ieee80211_band band = chan->band;
+       int delta;
+       int i;
+
+       memset(regs, '\0', sizeof(regs));
+
+       /* TODO: adapt TX power reduction from the rt28xx code */
+
+       /* calculate temperature compensation delta */
+       delta = rt2800_get_gain_calibration_delta(rt2x00dev);
+
+       if (band == IEEE80211_BAND_5GHZ)
+               offset = 16;
+       else
+               offset = 0;
+
+       if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
+               offset += 8;
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset, &eeprom);
+
+       /* CCK 1MBS,2MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK1_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK1_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_CCK1_CH2, txpower);
+
+       /* CCK 5.5MBS,11MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK5_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_CCK5_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_CCK5_CH2, txpower);
+
+       /* OFDM 6MBS,9MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM6_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM6_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower);
+
+       /* OFDM 12MBS,18MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM12_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_IDX],
+                          TX_PWR_CFG_0_OFDM12_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_0_EXT_IDX],
+                          TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 1, &eeprom);
+
+       /* OFDM 24MBS,36MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM24_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM24_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower);
+
+       /* OFDM 48MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM48_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_OFDM48_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower);
+
+       /* OFDM 54MBS */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_OFDM54_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 2, &eeprom);
+
+       /* MCS 0,1 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS0_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS0_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_MCS0_CH2, txpower);
+
+       /* MCS 2,3 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS2_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_IDX],
+                          TX_PWR_CFG_1_MCS2_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_1_EXT_IDX],
+                          TX_PWR_CFG_1_EXT_MCS2_CH2, txpower);
+
+       /* MCS 4,5 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS4_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS4_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS4_CH2, txpower);
+
+       /* MCS 6 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS6_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS6_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS6_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 3, &eeprom);
+
+       /* MCS 7 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_7_IDX],
+                          TX_PWR_CFG_7_MCS7_CH2, txpower);
+
+       /* MCS 8,9 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS8_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS8_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS8_CH2, txpower);
+
+       /* MCS 10,11 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS10_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_IDX],
+                          TX_PWR_CFG_2_MCS10_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_2_EXT_IDX],
+                          TX_PWR_CFG_2_EXT_MCS10_CH2, txpower);
+
+       /* MCS 12,13 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS12_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS12_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_MCS12_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 4, &eeprom);
+
+       /* MCS 14 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS14_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_MCS14_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_MCS14_CH2, txpower);
+
+       /* MCS 15 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS15_CH2, txpower);
+
+       /* MCS 16,17 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS16_CH2, txpower);
+
+       /* MCS 18,19 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_5_IDX],
+                          TX_PWR_CFG_5_MCS18_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 5, &eeprom);
+
+       /* MCS 20,21 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS20_CH2, txpower);
+
+       /* MCS 22 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_6_IDX],
+                          TX_PWR_CFG_6_MCS22_CH2, txpower);
+
+       /* MCS 23 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_8_IDX],
+                          TX_PWR_CFG_8_MCS23_CH2, txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 6, &eeprom);
+
+       /* STBC, MCS 0,1 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC0_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC0_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_STBC0_CH2, txpower);
+
+       /* STBC, MCS 2,3 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC2_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_IDX],
+                          TX_PWR_CFG_3_STBC2_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_3_EXT_IDX],
+                          TX_PWR_CFG_3_EXT_STBC2_CH2, txpower);
+
+       /* STBC, MCS 4,5 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0,
+                          txpower);
+
+       /* STBC, MCS 6 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2,
+                          txpower);
+
+       /* read the next four txpower values */
+       rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                     offset + 7, &eeprom);
+
+       /* STBC, MCS 7 */
+       txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+       txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+                                           txpower, delta);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH0, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH1, txpower);
+       rt2x00_set_field32(&regs[TX_PWR_CFG_9_IDX],
+                          TX_PWR_CFG_9_STBC7_CH2, txpower);
+
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]);
+
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT,
+                             regs[TX_PWR_CFG_0_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT,
+                             regs[TX_PWR_CFG_1_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT,
+                             regs[TX_PWR_CFG_2_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT,
+                             regs[TX_PWR_CFG_3_EXT_IDX]);
+       rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT,
+                             regs[TX_PWR_CFG_4_EXT_IDX]);
+
+       for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++)
+               rt2x00_dbg(rt2x00dev,
+                          "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n",
+                          (band == IEEE80211_BAND_5GHZ) ? '5' : '2',
+                          (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ?
+                                                               '4' : '2',
+                          (i > TX_PWR_CFG_9_IDX) ?
+                                       (i - TX_PWR_CFG_9_IDX - 1) : i,
+                          (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "",
+                          (unsigned long) regs[i]);
+}
+
 /*
  * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and
  * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values,
@@ -3010,9 +4044,9 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
  * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to
  * current conditions (i.e. band, bandwidth, temperature, user settings).
  */
-static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
-                                 struct ieee80211_channel *chan,
-                                 int power_level)
+static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev,
+                                        struct ieee80211_channel *chan,
+                                        int power_level)
 {
        u8 txpower, r1;
        u16 eeprom;
@@ -3080,8 +4114,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
                rt2800_register_read(rt2x00dev, offset, &reg);
 
                /* read the next four txpower values */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             i, &eeprom);
 
                is_rate_b = i ? 0 : 1;
                /*
@@ -3129,8 +4163,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
                rt2x00_set_field32(&reg, TX_PWR_CFG_RATE3, txpower);
 
                /* read the next four txpower values */
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1,
-                                  &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+                                             i + 1, &eeprom);
 
                is_rate_b = 0;
                /*
@@ -3184,6 +4218,16 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
        }
 }
 
+static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
+                                 struct ieee80211_channel *chan,
+                                 int power_level)
+{
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level);
+       else
+               rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level);
+}
+
 void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev)
 {
        rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan,
@@ -3219,6 +4263,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
                rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
                break;
+       case RF3053:
        case RF3290:
        case RF5360:
        case RF5370:
@@ -3442,17 +4487,25 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                return ret;
 
        rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
+       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
+                          rt2800_get_beacon_offset(rt2x00dev, 0));
+       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
+                          rt2800_get_beacon_offset(rt2x00dev, 1));
+       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
+                          rt2800_get_beacon_offset(rt2x00dev, 2));
+       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
+                          rt2800_get_beacon_offset(rt2x00dev, 3));
        rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
 
        rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
+       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
+                          rt2800_get_beacon_offset(rt2x00dev, 4));
+       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
+                          rt2800_get_beacon_offset(rt2x00dev, 5));
+       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
+                          rt2800_get_beacon_offset(rt2x00dev, 6));
+       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
+                          rt2800_get_beacon_offset(rt2x00dev, 7));
        rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
 
        rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
@@ -3528,7 +4581,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
-                       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
                        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
                                rt2800_register_write(rt2x00dev, TX_SW_CFG2,
                                                      0x0000002c);
@@ -3559,6 +4613,23 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        } else if (rt2x00_rt(rt2x00dev, RT3572)) {
                rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
                rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
+       } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+               rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402);
+               rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
+               if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) {
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
+                       if (rt2x00_get_field16(eeprom,
+                                              EEPROM_NIC_CONF1_DAC_TEST))
+                               rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                                     0x0000001f);
+                       else
+                               rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                                     0x0000000f);
+               } else {
+                       rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+                                             0x00000000);
+               }
        } else if (rt2x00_rt(rt2x00dev, RT5390) ||
                   rt2x00_rt(rt2x00dev, RT5392) ||
                   rt2x00_rt(rt2x00dev, RT5592)) {
@@ -3786,14 +4857,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        /*
         * Clear all beacons
         */
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE0);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE1);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE2);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE3);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE4);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE5);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE6);
-       rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE7);
+       for (i = 0; i < 8; i++)
+               rt2800_clear_beacon_register(rt2x00dev, i);
 
        if (rt2x00_is_usb(rt2x00dev)) {
                rt2800_register_read(rt2x00dev, US_CYC_CNT, &reg);
@@ -3989,7 +5054,7 @@ static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
        u8 value;
 
        rt2800_bbp_read(rt2x00dev, 138, &value);
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
                value |= 0x20;
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
@@ -4332,6 +5397,22 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
        rt2800_disable_unused_dac_adc(rt2x00dev);
 }
 
+static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev)
+{
+       rt2800_init_bbp_early(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 79, 0x13);
+       rt2800_bbp_write(rt2x00dev, 80, 0x05);
+       rt2800_bbp_write(rt2x00dev, 81, 0x33);
+       rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+       rt2800_bbp_write(rt2x00dev, 84, 0x19);
+
+       /* Enable DC filter */
+       if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E))
+               rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
+
 static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
 {
        int ant, div_mode;
@@ -4402,7 +5483,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
 
        rt2800_disable_unused_dac_adc(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
        div_mode = rt2x00_get_field16(eeprom,
                                      EEPROM_NIC_CONF1_ANT_DIVERSITY);
        ant = (div_mode == 3) ? 1 : 0;
@@ -4488,7 +5569,7 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
 
        rt2800_bbp4_mac_if_ctrl(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
        div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
        ant = (div_mode == 3) ? 1 : 0;
        rt2800_bbp_read(rt2x00dev, 152, &value);
@@ -4547,6 +5628,9 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
        case RT3572:
                rt2800_init_bbp_3572(rt2x00dev);
                break;
+       case RT3593:
+               rt2800_init_bbp_3593(rt2x00dev);
+               return;
        case RT5390:
        case RT5392:
                rt2800_init_bbp_53xx(rt2x00dev);
@@ -4557,7 +5641,8 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
        }
 
        for (i = 0; i < EEPROM_BBP_SIZE; i++) {
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
+               rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i,
+                                             &eeprom);
 
                if (eeprom != 0xffff && eeprom != 0x0000) {
                        reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
@@ -4728,7 +5813,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
        if (rt2x00_rt(rt2x00dev, RT3090)) {
                /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
                rt2800_bbp_read(rt2x00dev, 138, &bbp);
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
                if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
                        rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0);
                if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
@@ -4771,6 +5856,42 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
        }
 }
 
+static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev)
+{
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u8 rfcsr;
+       u8 tx_gain;
+
+       rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+       tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g,
+                                   RFCSR17_TXMIXER_GAIN);
+       rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain);
+       rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 38, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0);
+       rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+       rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+       rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+       rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+       rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+       /* TODO: enable stream mode */
+}
+
 static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev)
 {
        u8 reg;
@@ -4778,7 +5899,7 @@ static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev)
 
        /*  Turn off unused DAC1 and ADC1 to reduce power consumption */
        rt2800_bbp_read(rt2x00dev, 138, &reg);
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
                rt2x00_set_field8(&reg, BBP138_RX_ADC1, 0);
        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
@@ -4884,7 +6005,8 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
                if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
                    rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) {
-                       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+                       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+                                          &eeprom);
                        if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
                                rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 3);
                        else
@@ -5152,6 +6274,136 @@ static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev)
        rt2800_normal_mode_setup_3xxx(rt2x00dev);
 }
 
+static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev)
+{
+       u8 bbp;
+       bool txbf_enabled = false; /* FIXME */
+
+       rt2800_bbp_read(rt2x00dev, 105, &bbp);
+       if (rt2x00dev->default_ant.rx_chain_num == 1)
+               rt2x00_set_field8(&bbp, BBP105_MLD, 0);
+       else
+               rt2x00_set_field8(&bbp, BBP105_MLD, 1);
+       rt2800_bbp_write(rt2x00dev, 105, bbp);
+
+       rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+       rt2800_bbp_write(rt2x00dev, 92, 0x02);
+       rt2800_bbp_write(rt2x00dev, 82, 0x82);
+       rt2800_bbp_write(rt2x00dev, 106, 0x05);
+       rt2800_bbp_write(rt2x00dev, 104, 0x92);
+       rt2800_bbp_write(rt2x00dev, 88, 0x90);
+       rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+       rt2800_bbp_write(rt2x00dev, 47, 0x48);
+       rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+       if (txbf_enabled)
+               rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+       else
+               rt2800_bbp_write(rt2x00dev, 163, 0x9d);
+
+       /* SNR mapping */
+       rt2800_bbp_write(rt2x00dev, 142, 6);
+       rt2800_bbp_write(rt2x00dev, 143, 160);
+       rt2800_bbp_write(rt2x00dev, 142, 7);
+       rt2800_bbp_write(rt2x00dev, 143, 161);
+       rt2800_bbp_write(rt2x00dev, 142, 8);
+       rt2800_bbp_write(rt2x00dev, 143, 162);
+
+       /* ADC/DAC control */
+       rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+       /* RX AGC energy lower bound in log2 */
+       rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+       /* FIXME: BBP 105 owerwrite? */
+       rt2800_bbp_write(rt2x00dev, 105, 0x04);
+
+}
+
+static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev)
+{
+       struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+       u32 reg;
+       u8 rfcsr;
+
+       /* Disable GPIO #4 and #7 function for LAN PE control */
+       rt2800_register_read(rt2x00dev, GPIO_SWITCH, &reg);
+       rt2x00_set_field32(&reg, GPIO_SWITCH_4, 0);
+       rt2x00_set_field32(&reg, GPIO_SWITCH_7, 0);
+       rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg);
+
+       /* Initialize default register values */
+       rt2800_rfcsr_write(rt2x00dev, 1, 0x03);
+       rt2800_rfcsr_write(rt2x00dev, 3, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 5, 0x00);
+       rt2800_rfcsr_write(rt2x00dev, 6, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 8, 0xf1);
+       rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
+       rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+       rt2800_rfcsr_write(rt2x00dev, 11, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 12, 0x4e);
+       rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+       rt2800_rfcsr_write(rt2x00dev, 18, 0x40);
+       rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+       rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+       rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+       rt2800_rfcsr_write(rt2x00dev, 32, 0x78);
+       rt2800_rfcsr_write(rt2x00dev, 33, 0x3b);
+       rt2800_rfcsr_write(rt2x00dev, 34, 0x3c);
+       rt2800_rfcsr_write(rt2x00dev, 35, 0xe0);
+       rt2800_rfcsr_write(rt2x00dev, 38, 0x86);
+       rt2800_rfcsr_write(rt2x00dev, 39, 0x23);
+       rt2800_rfcsr_write(rt2x00dev, 44, 0xd3);
+       rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+       rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+       rt2800_rfcsr_write(rt2x00dev, 49, 0x8e);
+       rt2800_rfcsr_write(rt2x00dev, 50, 0x86);
+       rt2800_rfcsr_write(rt2x00dev, 51, 0x75);
+       rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+       rt2800_rfcsr_write(rt2x00dev, 53, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 54, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 55, 0x18);
+       rt2800_rfcsr_write(rt2x00dev, 56, 0xdb);
+       rt2800_rfcsr_write(rt2x00dev, 57, 0x6e);
+
+       /* Initiate calibration */
+       /* TODO: use rt2800_rf_init_calibration ? */
+       rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1);
+       rt2800_rfcsr_write(rt2x00dev, 2, rfcsr);
+
+       rt2800_adjust_freq_offset(rt2x00dev);
+
+       rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr);
+       rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1);
+       rt2800_rfcsr_write(rt2x00dev, 18, rfcsr);
+
+       rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+       rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 3);
+       rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
+       rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+       usleep_range(1000, 1500);
+       rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+       rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 0);
+       rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+
+       /* Set initial values for RX filter calibration */
+       drv_data->calibration_bw20 = 0x1f;
+       drv_data->calibration_bw40 = 0x2f;
+
+       /* Save BBP 25 & 26 values for later use in channel switching */
+       rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25);
+       rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26);
+
+       rt2800_led_open_drain_enable(rt2x00dev);
+       rt2800_normal_mode_setup_3593(rt2x00dev);
+
+       rt3593_post_bbp_init(rt2x00dev);
+
+       /* TODO: enable stream mode support */
+}
+
 static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
 {
        rt2800_rf_init_calibration(rt2x00dev, 2);
@@ -5380,6 +6632,9 @@ static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        case RT3572:
                rt2800_init_rfcsr_3572(rt2x00dev);
                break;
+       case RT3593:
+               rt2800_init_rfcsr_3593(rt2x00dev);
+               break;
        case RT5390:
                rt2800_init_rfcsr_5390(rt2x00dev);
                break;
@@ -5456,15 +6711,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
        /*
         * Initialize LED control
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
        rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff,
                           word & 0xff, (word >> 8) & 0xff);
 
@@ -5560,6 +6815,34 @@ int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
 }
 EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
 
+static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev)
+{
+       u16 word;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return 0;
+
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
+       if ((word & 0x00ff) != 0x00ff)
+               return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
+
+       return 0;
+}
+
+static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev)
+{
+       u16 word;
+
+       if (rt2x00_rt(rt2x00dev, RT3593))
+               return 0;
+
+       rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
+       if ((word & 0x00ff) != 0x00ff)
+               return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
+
+       return 0;
+}
+
 static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
 {
        struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
@@ -5578,18 +6861,18 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Start validation of the data that has been read.
         */
-       mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
+       mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
        if (!is_valid_ether_addr(mac)) {
                eth_random_addr(mac);
                rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
        if (word == 0xffff) {
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
                rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word);
        } else if (rt2x00_rt(rt2x00dev, RT2860) ||
                   rt2x00_rt(rt2x00dev, RT2872)) {
@@ -5598,10 +6881,10 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                 */
                if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2)
                        rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
        if (word == 0xffff) {
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0);
@@ -5618,24 +6901,24 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0);
                rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
                rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word);
        }
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
        if ((word & 0x00ff) == 0x00ff) {
                rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
                rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word);
        }
        if ((word & 0xff00) == 0xff00) {
                rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE,
                                   LED_MODE_TXRX_ACTIVITY);
                rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
-               rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
                rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word);
        }
 
@@ -5644,56 +6927,61 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
         * lna0 as correct value. Note that EEPROM_LNA
         * is never validated.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
        default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
-       if ((word & 0x00ff) != 0x00ff) {
-               drv_data->txmixer_gain_24g =
-                       rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
-       } else {
-               drv_data->txmixer_gain_24g = 0;
-       }
+       drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0);
-       if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
-           rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
-               rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
-                                  default_lna_gain);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
-
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
-       if ((word & 0x00ff) != 0x00ff) {
-               drv_data->txmixer_gain_5g =
-                       rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
-       } else {
-               drv_data->txmixer_gain_5g = 0;
+       if (!rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
+                                          default_lna_gain);
        }
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
+       drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev);
+
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
        if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10)
                rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0);
-       if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
-           rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
-               rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
-                                  default_lna_gain);
-       rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+       if (!rt2x00_rt(rt2x00dev, RT3593)) {
+               if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
+                                          default_lna_gain);
+       }
+       rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+
+       if (rt2x00_rt(rt2x00dev, RT3593)) {
+               rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word);
+               if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+                                          default_lna_gain);
+               if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 ||
+                   rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff)
+                       rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+                                          default_lna_gain);
+               rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word);
+       }
 
        return 0;
 }
@@ -5707,7 +6995,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Read EEPROM word for configuration.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
 
        /*
         * Identify RF chipset by EEPROM value
@@ -5717,7 +7005,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        if (rt2x00_rt(rt2x00dev, RT3290) ||
            rt2x00_rt(rt2x00dev, RT5390) ||
            rt2x00_rt(rt2x00dev, RT5392))
-               rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
+               rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
        else
                rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
 
@@ -5731,6 +7019,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        case RF3021:
        case RF3022:
        case RF3052:
+       case RF3053:
        case RF3290:
        case RF3320:
        case RF3322:
@@ -5757,7 +7046,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->default_ant.rx_chain_num =
            rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH);
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
 
        if (rt2x00_rt(rt2x00dev, RT3070) ||
            rt2x00_rt(rt2x00dev, RT3090) ||
@@ -5810,7 +7099,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Read frequency offset and RF programming sequence.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
        rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET);
 
        /*
@@ -5827,7 +7116,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Check if support EIRP tx power limit feature.
         */
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
 
        if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) <
                                        EIRP_MAX_TX_POWER_LIMIT)
@@ -6109,12 +7398,79 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
        {196, 83, 0, 12, 1},
 };
 
+static const struct rf_channel rf_vals_3053[] = {
+       /* Channel, N, R, K */
+       {1, 241, 2, 2},
+       {2, 241, 2, 7},
+       {3, 242, 2, 2},
+       {4, 242, 2, 7},
+       {5, 243, 2, 2},
+       {6, 243, 2, 7},
+       {7, 244, 2, 2},
+       {8, 244, 2, 7},
+       {9, 245, 2, 2},
+       {10, 245, 2, 7},
+       {11, 246, 2, 2},
+       {12, 246, 2, 7},
+       {13, 247, 2, 2},
+       {14, 248, 2, 4},
+
+       {36, 0x56, 0, 4},
+       {38, 0x56, 0, 6},
+       {40, 0x56, 0, 8},
+       {44, 0x57, 0, 0},
+       {46, 0x57, 0, 2},
+       {48, 0x57, 0, 4},
+       {52, 0x57, 0, 8},
+       {54, 0x57, 0, 10},
+       {56, 0x58, 0, 0},
+       {60, 0x58, 0, 4},
+       {62, 0x58, 0, 6},
+       {64, 0x58, 0, 8},
+
+       {100, 0x5B, 0, 8},
+       {102, 0x5B, 0, 10},
+       {104, 0x5C, 0, 0},
+       {108, 0x5C, 0, 4},
+       {110, 0x5C, 0, 6},
+       {112, 0x5C, 0, 8},
+
+       /* NOTE: Channel 114 has been removed intentionally.
+        * The EEPROM contains no TX power values for that,
+        * and it is disabled in the vendor driver as well.
+        */
+
+       {116, 0x5D, 0, 0},
+       {118, 0x5D, 0, 2},
+       {120, 0x5D, 0, 4},
+       {124, 0x5D, 0, 8},
+       {126, 0x5D, 0, 10},
+       {128, 0x5E, 0, 0},
+       {132, 0x5E, 0, 4},
+       {134, 0x5E, 0, 6},
+       {136, 0x5E, 0, 8},
+       {140, 0x5F, 0, 0},
+
+       {149, 0x5F, 0, 9},
+       {151, 0x5F, 0, 11},
+       {153, 0x60, 0, 1},
+       {157, 0x60, 0, 5},
+       {159, 0x60, 0, 7},
+       {161, 0x60, 0, 9},
+       {165, 0x61, 0, 1},
+       {167, 0x61, 0, 3},
+       {169, 0x61, 0, 5},
+       {171, 0x61, 0, 7},
+       {173, 0x61, 0, 9},
+};
+
 static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
        struct channel_info *info;
        char *default_power1;
        char *default_power2;
+       char *default_power3;
        unsigned int i;
        u16 eeprom;
        u32 reg;
@@ -6149,7 +7505,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
        SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
-                               rt2x00_eeprom_addr(rt2x00dev,
+                               rt2800_eeprom_addr(rt2x00dev,
                                                   EEPROM_MAC_ADDR_0));
 
        /*
@@ -6165,7 +7521,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->max_report_rates = 7;
        rt2x00dev->hw->max_rate_tries = 1;
 
-       rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+       rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
 
        /*
         * Initialize hw_mode information.
@@ -6200,6 +7556,10 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_3x);
                spec->channels = rf_vals_3x;
+       } else if (rt2x00_rf(rt2x00dev, RF3053)) {
+               spec->supported_bands |= SUPPORT_BAND_5GHZ;
+               spec->num_channels = ARRAY_SIZE(rf_vals_3053);
+               spec->channels = rf_vals_3053;
        } else if (rt2x00_rf(rt2x00dev, RF5592)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
 
@@ -6265,21 +7625,40 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 
        spec->channels_info = info;
 
-       default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
-       default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+       default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+       default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+
+       if (rt2x00dev->default_ant.tx_chain_num > 2)
+               default_power3 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_EXT_TXPOWER_BG3);
+       else
+               default_power3 = NULL;
 
        for (i = 0; i < 14; i++) {
                info[i].default_power1 = default_power1[i];
                info[i].default_power2 = default_power2[i];
+               if (default_power3)
+                       info[i].default_power3 = default_power3[i];
        }
 
        if (spec->num_channels > 14) {
-               default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
-               default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+               default_power1 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_TXPOWER_A1);
+               default_power2 = rt2800_eeprom_addr(rt2x00dev,
+                                                   EEPROM_TXPOWER_A2);
+
+               if (rt2x00dev->default_ant.tx_chain_num > 2)
+                       default_power3 =
+                               rt2800_eeprom_addr(rt2x00dev,
+                                                  EEPROM_EXT_TXPOWER_A3);
+               else
+                       default_power3 = NULL;
 
                for (i = 14; i < spec->num_channels; i++) {
                        info[i].default_power1 = default_power1[i - 14];
                        info[i].default_power2 = default_power2[i - 14];
+                       if (default_power3)
+                               info[i].default_power3 = default_power3[i - 14];
                }
        }
 
@@ -6290,6 +7669,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        case RF3022:
        case RF3320:
        case RF3052:
+       case RF3053:
        case RF3290:
        case RF5360:
        case RF5370:
@@ -6328,6 +7708,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
        case RT3352:
        case RT3390:
        case RT3572:
+       case RT3593:
        case RT5390:
        case RT5392:
        case RT5592:
index 6ec7394..a94ba44 100644 (file)
@@ -226,4 +226,8 @@ int rt2800_get_survey(struct ieee80211_hw *hw, int idx,
                      struct survey_info *survey);
 void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev);
 
+void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev,
+                              unsigned short *txwi_size,
+                              unsigned short *rxwi_size);
+
 #endif /* RT2800LIB_H */
index 0005562..f8f2abb 100644 (file)
@@ -507,9 +507,13 @@ static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
 
        if (rt2x00_is_pcie(rt2x00dev) &&
-           (rt2x00_rt(rt2x00dev, RT3572) ||
+           (rt2x00_rt(rt2x00dev, RT3090) ||
+            rt2x00_rt(rt2x00dev, RT3390) ||
+            rt2x00_rt(rt2x00dev, RT3572) ||
+            rt2x00_rt(rt2x00dev, RT3593) ||
             rt2x00_rt(rt2x00dev, RT5390) ||
-            rt2x00_rt(rt2x00dev, RT5392))) {
+            rt2x00_rt(rt2x00dev, RT5392) ||
+            rt2x00_rt(rt2x00dev, RT5592))) {
                rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, &reg);
                rt2x00_set_field32(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
                rt2x00_set_field32(&reg, AUX_CTRL_WAKE_PCIE_EN, 1);
@@ -1189,12 +1193,17 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
 
 static void rt2800pci_queue_init(struct data_queue *queue)
 {
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
+
+       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
+
        switch (queue->qid) {
        case QID_RX:
                queue->limit = 128;
                queue->data_size = AGGREGATION_SIZE;
                queue->desc_size = RXD_DESC_SIZE;
-               queue->winfo_size = RXWI_DESC_SIZE_4WORDS;
+               queue->winfo_size = rxwi_size;
                queue->priv_size = sizeof(struct queue_entry_priv_mmio);
                break;
 
@@ -1205,7 +1214,7 @@ static void rt2800pci_queue_init(struct data_queue *queue)
                queue->limit = 64;
                queue->data_size = AGGREGATION_SIZE;
                queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+               queue->winfo_size = txwi_size;
                queue->priv_size = sizeof(struct queue_entry_priv_mmio);
                break;
 
@@ -1213,7 +1222,7 @@ static void rt2800pci_queue_init(struct data_queue *queue)
                queue->limit = 8;
                queue->data_size = 0; /* No DMA required for beacons */
                queue->desc_size = TXD_DESC_SIZE;
-               queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+               queue->winfo_size = txwi_size;
                queue->priv_size = sizeof(struct queue_entry_priv_mmio);
                break;
 
index 840833b..338034e 100644 (file)
@@ -854,13 +854,7 @@ static void rt2800usb_queue_init(struct data_queue *queue)
        struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
        unsigned short txwi_size, rxwi_size;
 
-       if (rt2x00_rt(rt2x00dev, RT5592)) {
-               txwi_size = TXWI_DESC_SIZE_5WORDS;
-               rxwi_size = RXWI_DESC_SIZE_6WORDS;
-       } else {
-               txwi_size = TXWI_DESC_SIZE_4WORDS;
-               rxwi_size = RXWI_DESC_SIZE_4WORDS;
-       }
+       rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
 
        switch (queue->qid) {
        case QID_RX:
@@ -1194,6 +1188,40 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Zinwell */
        { USB_DEVICE(0x5a57, 0x0284) },
 #endif
+#ifdef CONFIG_RT2800USB_RT3573
+       /* AirLive */
+       { USB_DEVICE(0x1b75, 0x7733) },
+       /* ASUS */
+       { USB_DEVICE(0x0b05, 0x17bc) },
+       { USB_DEVICE(0x0b05, 0x17ad) },
+       /* Belkin */
+       { USB_DEVICE(0x050d, 0x1103) },
+       /* Cameo */
+       { USB_DEVICE(0x148f, 0xf301) },
+       /* Edimax */
+       { USB_DEVICE(0x7392, 0x7733) },
+       /* Hawking */
+       { USB_DEVICE(0x0e66, 0x0020) },
+       { USB_DEVICE(0x0e66, 0x0021) },
+       /* I-O DATA */
+       { USB_DEVICE(0x04bb, 0x094e) },
+       /* Linksys */
+       { USB_DEVICE(0x13b1, 0x003b) },
+       /* Logitec */
+       { USB_DEVICE(0x0789, 0x016b) },
+       /* NETGEAR */
+       { USB_DEVICE(0x0846, 0x9012) },
+       { USB_DEVICE(0x0846, 0x9019) },
+       /* Planex */
+       { USB_DEVICE(0x2019, 0xed19) },
+       /* Ralink */
+       { USB_DEVICE(0x148f, 0x3573) },
+       /* Sitecom */
+       { USB_DEVICE(0x0df6, 0x0067) },
+       { USB_DEVICE(0x0df6, 0x006a) },
+       /* ZyXEL */
+       { USB_DEVICE(0x0586, 0x3421) },
+#endif
 #ifdef CONFIG_RT2800USB_RT53XX
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
index ee3fc57..fe4c572 100644 (file)
@@ -211,6 +211,7 @@ struct channel_info {
        short max_power;
        short default_power1;
        short default_power2;
+       short default_power3;
 };
 
 /*
index aa95c6c..6c8a33b 100644 (file)
@@ -936,7 +936,7 @@ void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index)
        spin_unlock_irqrestore(&queue->index_lock, irqflags);
 }
 
-void rt2x00queue_pause_queue_nocheck(struct data_queue *queue)
+static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue)
 {
        switch (queue->qid) {
        case QID_AC_VO:
index 91a04e2..fc207b2 100644 (file)
@@ -3,10 +3,10 @@
  * Linux device driver for RTL8180 / RTL8185
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * Thanks to Realtek for their support!
  *
@@ -32,7 +32,7 @@
 #include "grf5101.h"
 
 MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
 MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver");
 MODULE_LICENSE("GPL");
 
index 077ff92..dc84569 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Radio tuning for GCT GRF5101 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index 7664711..4d80a27 100644 (file)
@@ -4,7 +4,7 @@
 /*
  * Radio tuning for GCT GRF5101 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index 4715000..a63c443 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Radio tuning for Maxim max2820 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index 61cf6d1..8e982b7 100644 (file)
@@ -4,7 +4,7 @@
 /*
  * Radio tuning for Maxim max2820 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index cc2a541..ee638d0 100644 (file)
@@ -3,10 +3,10 @@
  * Radio tuning for RTL8225 on RTL8180
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8180 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * Thanks to Realtek for their support!
  *
index b3ec40f..7614d9c 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Radio tuning for Philips SA2400 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index a4aaa0d..fb0093f 100644 (file)
@@ -4,7 +4,7 @@
 /*
  * Radio tuning for Philips SA2400 on RTL8180
  *
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Code from the BSD driver and the rtl8181 project have been
  * very useful to understand certain things
index f49220e..841fb9d 100644 (file)
@@ -2,10 +2,10 @@
  * Linux device driver for RTL8187
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * The driver was extended to the RTL8187B in 2008 by:
  *     Herton Ronaldo Krzesinski <herton@mandriva.com.br>
@@ -37,7 +37,7 @@
 #include "rfkill.h"
 
 MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
 MODULE_AUTHOR("Herton Ronaldo Krzesinski <herton@mandriva.com.br>");
 MODULE_AUTHOR("Hin-Tak Leung <htl10@users.sourceforge.net>");
 MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
index e19a20a..56aee06 100644 (file)
@@ -2,10 +2,10 @@
  * Definitions for RTL8187 hardware
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * 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
index f0bf35f..a26193a 100644 (file)
@@ -2,10 +2,10 @@
  * Radio tuning for RTL8225 on RTL8187
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * Magic delays, register offsets, and phy value tables below are
  * taken from the original r8187 driver sources.  Thanks to Realtek
index 20c5b6e..141afb0 100644 (file)
@@ -2,10 +2,10 @@
  * Radio tuning definitions for RTL8225 on RTL8187
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * 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
index 1615f63..ce23dfd 100644 (file)
@@ -2,10 +2,10 @@
  * Definitions for RTL818x hardware
  *
  * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
  *
  * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  *
  * 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
index 298b615..f646b75 100644 (file)
@@ -688,7 +688,7 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
        find_p2p_ie = true;
        /*to find noa ie*/
        while (ie + 1 < end) {
-               noa_len = READEF2BYTE(&ie[1]);
+               noa_len = READEF2BYTE((__le16 *)&ie[1]);
                if (ie + 3 + ie[1] > end)
                        return;
 
@@ -717,13 +717,13 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
                                                 READEF1BYTE(ie+index);
                                        index += 1;
                                        p2pinfo->noa_duration[i] =
-                                                READEF4BYTE(ie+index);
+                                                READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                        p2pinfo->noa_interval[i] =
-                                                READEF4BYTE(ie+index);
+                                                READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                        p2pinfo->noa_start_time[i] =
-                                                READEF4BYTE(ie+index);
+                                                READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                }
 
@@ -780,7 +780,7 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
        RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
        /*to find noa ie*/
        while (ie + 1 < end) {
-               noa_len = READEF2BYTE(&ie[1]);
+               noa_len = READEF2BYTE((__le16 *)&ie[1]);
                if (ie + 3 + ie[1] > end)
                        return;
 
@@ -809,13 +809,13 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
                                                         READEF1BYTE(ie+index);
                                        index += 1;
                                        p2pinfo->noa_duration[i] =
-                                                        READEF4BYTE(ie+index);
+                                                        READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                        p2pinfo->noa_interval[i] =
-                                                        READEF4BYTE(ie+index);
+                                                        READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                        p2pinfo->noa_start_time[i] =
-                                                        READEF4BYTE(ie+index);
+                                                        READEF4BYTE((__le32 *)ie+index);
                                        index += 4;
                                }
 
index f9f059d..a98acef 100644 (file)
@@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv,
 
 static void rtl_rate_init(void *ppriv,
                          struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta)
 {
 }
index 8e3ec1e..0f7812e 100644 (file)
@@ -109,5 +109,8 @@ void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
                         u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
 bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw);
+void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
+                                struct ieee80211_sta *sta,
+                                u8 rssi_level);
 
 #endif
index 262e1e4..a1310ab 100644 (file)
@@ -49,8 +49,5 @@ bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw,
 u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw,
                            enum radio_path rfpath, u32 regaddr, u32 bitmask);
 void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
-void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
-                                struct ieee80211_sta *sta,
-                                u8 rssi_level);
 
 #endif
index b8db55c..d1b19c3 100644 (file)
@@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
 
 #ifdef CONFIG_PM
 static int
-wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
+wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
 {
        int num_fields = 0, in_field = 0, fields_size = 0;
        int i, pattern_len = 0;
@@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
  * Allocates an RX filter returned through f
  * which needs to be freed using rx_filter_free()
  */
-static int wl1271_convert_wowlan_pattern_to_rx_filter(
-       struct cfg80211_wowlan_trig_pkt_pattern *p,
-       struct wl12xx_rx_filter **f)
+static int
+wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
+                                          struct wl12xx_rx_filter **f)
 {
        int i, j, ret = 0;
        struct wl12xx_rx_filter *filter;
@@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl,
 
        /* Translate WoWLAN patterns into filters */
        for (i = 0; i < wow->n_patterns; i++) {
-               struct cfg80211_wowlan_trig_pkt_pattern *p;
+               struct cfg80211_pkt_pattern *p;
                struct wl12xx_rx_filter *filter = NULL;
 
                p = &wow->patterns[i];
index f344276..527590f 100644 (file)
@@ -356,7 +356,8 @@ out:
        return ret;
 }
 
-int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
+int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 void *data, int len)
 {
        struct wl1271 *wl = hw->priv;
        struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
index 8071654..61d8434 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <net/mac80211.h>
 
-int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len);
+int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 void *data, int len);
 
 #endif /* __WL1271_TESTMODE_H__ */
index b8ba1f9..d39c417 100644 (file)
@@ -75,8 +75,10 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw)
         len = fw_entry->size;
 
        buf = kmalloc(1024, GFP_ATOMIC);
-       if (!buf)
+       if (!buf) {
+               err = -ENOMEM;
                goto exit;
+       }
        
        while (len > 0) {
                int translen = (len > 1024) ? 1024 : len;
@@ -1764,8 +1766,10 @@ static int zd1201_probe(struct usb_interface *interface,
        zd->endp_out2 = 2;
        zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
        zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!zd->rx_urb || !zd->tx_urb)
+       if (!zd->rx_urb || !zd->tx_urb) {
+               err = -ENOMEM;
                goto err_zd;
+       }
 
        mdelay(100);
        err = zd1201_drvr_start(zd);
index c5c30fb..9a53f13 100644 (file)
@@ -60,7 +60,7 @@ struct nfcsim {
 static struct nfcsim *dev0;
 static struct nfcsim *dev1;
 
-struct workqueue_struct *wq;
+static struct workqueue_struct *wq;
 
 static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
 {
@@ -481,7 +481,7 @@ static void nfcsim_free_device(struct nfcsim *dev)
        kfree(dev);
 }
 
-int __init nfcsim_init(void)
+static int __init nfcsim_init(void)
 {
        int rc;
 
@@ -522,7 +522,7 @@ exit:
        return rc;
 }
 
-void __exit nfcsim_exit(void)
+static void __exit nfcsim_exit(void)
 {
        nfcsim_cleanup_dev(dev0, 1);
        nfcsim_cleanup_dev(dev1, 1);
index daf92ac..5df730b 100644 (file)
@@ -83,12 +83,20 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 
 /* How much time we spend listening for initiators */
 #define PN533_LISTEN_TIME 2
+/* Delay between each poll frame (ms) */
+#define PN533_POLL_INTERVAL 10
 
-/* Standard pn533 frame definitions */
+/* Standard pn533 frame definitions (standard and extended)*/
 #define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \
                                        + 2) /* data[0] TFI, data[1] CC */
 #define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
 
+#define PN533_EXT_FRAME_HEADER_LEN (sizeof(struct pn533_ext_frame) \
+                                       + 2) /* data[0] TFI, data[1] CC */
+
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+#define PN533_CMD_DATAFRAME_MAXLEN     240     /* max data length (send) */
+
 /*
  * Max extended frame payload len, excluding TFI and CC
  * which are already in PN533_FRAME_HEADER_LEN.
@@ -99,6 +107,10 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
                                  Postamble (1) */
 #define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen])
 #define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+/* Half start code (3), LEN (4) should be 0xffff for extended frame */
+#define PN533_STD_IS_EXTENDED(hdr) ((hdr)->datalen == 0xFF \
+                                       && (hdr)->datalen_checksum == 0xFF)
+#define PN533_EXT_FRAME_CHECKSUM(f) (f->data[be16_to_cpu(f->datalen)])
 
 /* start of frame */
 #define PN533_STD_FRAME_SOF 0x00FF
@@ -124,7 +136,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83
 
 /* PN533 Commands */
-#define PN533_STD_FRAME_CMD(f) (f->data[1])
+#define PN533_FRAME_CMD(f) (f->data[1])
 
 #define PN533_CMD_GET_FIRMWARE_VERSION 0x02
 #define PN533_CMD_RF_CONFIGURATION 0x32
@@ -168,8 +180,9 @@ struct pn533_fw_version {
 #define PN533_CFGITEM_MAX_RETRIES 0x05
 #define PN533_CFGITEM_PASORI      0x82
 
-#define PN533_CFGITEM_RF_FIELD_ON  0x1
-#define PN533_CFGITEM_RF_FIELD_OFF 0x0
+#define PN533_CFGITEM_RF_FIELD_AUTO_RFCA 0x2
+#define PN533_CFGITEM_RF_FIELD_ON        0x1
+#define PN533_CFGITEM_RF_FIELD_OFF       0x0
 
 #define PN533_CONFIG_TIMING_102 0xb
 #define PN533_CONFIG_TIMING_204 0xc
@@ -257,7 +270,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                        .initiator_data.felica = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
-                               .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+                               .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
                                .tsn = 0x03,
                        },
                },
@@ -270,7 +283,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                        .initiator_data.felica = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
-                               .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+                               .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
                                .tsn = 0x03,
                        },
                 },
@@ -352,13 +365,16 @@ struct pn533 {
        struct urb *in_urb;
 
        struct sk_buff_head resp_q;
+       struct sk_buff_head fragment_skb;
 
        struct workqueue_struct *wq;
        struct work_struct cmd_work;
        struct work_struct cmd_complete_work;
-       struct work_struct poll_work;
-       struct work_struct mi_work;
+       struct delayed_work poll_work;
+       struct work_struct mi_rx_work;
+       struct work_struct mi_tx_work;
        struct work_struct tg_work;
+       struct work_struct rf_work;
 
        struct list_head cmd_queue;
        struct pn533_cmd *cmd;
@@ -366,6 +382,7 @@ struct pn533 {
        struct mutex cmd_lock;  /* protects cmd queue */
 
        void *cmd_complete_mi_arg;
+       void *cmd_complete_dep_arg;
 
        struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
        u8 poll_mod_count;
@@ -404,6 +421,15 @@ struct pn533_std_frame {
        u8 data[];
 } __packed;
 
+struct pn533_ext_frame {       /* Extended Information frame */
+       u8 preamble;
+       __be16 start_frame;
+       __be16 eif_flag;        /* fixed to 0xFFFF */
+       __be16 datalen;
+       u8 datalen_checksum;
+       u8 data[];
+} __packed;
+
 struct pn533_frame_ops {
        void (*tx_frame_init)(void *frame, u8 cmd_code);
        void (*tx_frame_finish)(void *frame);
@@ -411,7 +437,7 @@ struct pn533_frame_ops {
        int tx_header_len;
        int tx_tail_len;
 
-       bool (*rx_is_frame_valid)(void *frame);
+       bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev);
        int (*rx_frame_size)(void *frame);
        int rx_header_len;
        int rx_tail_len;
@@ -486,7 +512,7 @@ static void pn533_acr122_tx_update_payload_len(void *_frame, int len)
        frame->datalen += len;
 }
 
-static bool pn533_acr122_is_rx_frame_valid(void *_frame)
+static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
 {
        struct pn533_acr122_rx_frame *frame = _frame;
 
@@ -511,7 +537,7 @@ static u8 pn533_acr122_get_cmd_code(void *frame)
 {
        struct pn533_acr122_rx_frame *f = frame;
 
-       return PN533_STD_FRAME_CMD(f);
+       return PN533_FRAME_CMD(f);
 }
 
 static struct pn533_frame_ops pn533_acr122_frame_ops = {
@@ -530,6 +556,12 @@ static struct pn533_frame_ops pn533_acr122_frame_ops = {
        .get_cmd_code = pn533_acr122_get_cmd_code,
 };
 
+/* The rule: value(high byte) + value(low byte) + checksum = 0 */
+static inline u8 pn533_ext_checksum(u16 value)
+{
+       return ~(u8)(((value & 0xFF00) >> 8) + (u8)(value & 0xFF)) + 1;
+}
+
 /* The rule: value + checksum = 0 */
 static inline u8 pn533_std_checksum(u8 value)
 {
@@ -555,7 +587,7 @@ static void pn533_std_tx_frame_init(void *_frame, u8 cmd_code)
        frame->preamble = 0;
        frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF);
        PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT;
-       PN533_STD_FRAME_CMD(frame) = cmd_code;
+       PN533_FRAME_CMD(frame) = cmd_code;
        frame->datalen = 2;
 }
 
@@ -578,21 +610,41 @@ static void pn533_std_tx_update_payload_len(void *_frame, int len)
        frame->datalen += len;
 }
 
-static bool pn533_std_rx_frame_is_valid(void *_frame)
+static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev)
 {
        u8 checksum;
-       struct pn533_std_frame *frame = _frame;
+       struct pn533_std_frame *stdf = _frame;
 
-       if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
+       if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
                return false;
 
-       checksum = pn533_std_checksum(frame->datalen);
-       if (checksum != frame->datalen_checksum)
-               return false;
+       if (likely(!PN533_STD_IS_EXTENDED(stdf))) {
+               /* Standard frame code */
+               dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN;
 
-       checksum = pn533_std_data_checksum(frame->data, frame->datalen);
-       if (checksum != PN533_STD_FRAME_CHECKSUM(frame))
-               return false;
+               checksum = pn533_std_checksum(stdf->datalen);
+               if (checksum != stdf->datalen_checksum)
+                       return false;
+
+               checksum = pn533_std_data_checksum(stdf->data, stdf->datalen);
+               if (checksum != PN533_STD_FRAME_CHECKSUM(stdf))
+                       return false;
+       } else {
+               /* Extended */
+               struct pn533_ext_frame *eif = _frame;
+
+               dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN;
+
+               checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen));
+               if (checksum != eif->datalen_checksum)
+                       return false;
+
+               /* check data checksum */
+               checksum = pn533_std_data_checksum(eif->data,
+                                                  be16_to_cpu(eif->datalen));
+               if (checksum != PN533_EXT_FRAME_CHECKSUM(eif))
+                       return false;
+       }
 
        return true;
 }
@@ -612,6 +664,14 @@ static inline int pn533_std_rx_frame_size(void *frame)
 {
        struct pn533_std_frame *f = frame;
 
+       /* check for Extended Information frame */
+       if (PN533_STD_IS_EXTENDED(f)) {
+               struct pn533_ext_frame *eif = frame;
+
+               return sizeof(struct pn533_ext_frame)
+                       + be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN;
+       }
+
        return sizeof(struct pn533_std_frame) + f->datalen +
               PN533_STD_FRAME_TAIL_LEN;
 }
@@ -619,8 +679,12 @@ static inline int pn533_std_rx_frame_size(void *frame)
 static u8 pn533_std_get_cmd_code(void *frame)
 {
        struct pn533_std_frame *f = frame;
+       struct pn533_ext_frame *eif = frame;
 
-       return PN533_STD_FRAME_CMD(f);
+       if (PN533_STD_IS_EXTENDED(f))
+               return PN533_FRAME_CMD(eif);
+       else
+               return PN533_FRAME_CMD(f);
 }
 
 static struct pn533_frame_ops pn533_std_frame_ops = {
@@ -675,7 +739,7 @@ static void pn533_recv_response(struct urb *urb)
        print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
                             dev->ops->rx_frame_size(in_frame), false);
 
-       if (!dev->ops->rx_is_frame_valid(in_frame)) {
+       if (!dev->ops->rx_is_frame_valid(in_frame, dev)) {
                nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
                cmd->status = -EIO;
                goto sched_wq;
@@ -1657,7 +1721,56 @@ static void pn533_listen_mode_timer(unsigned long data)
 
        pn533_poll_next_mod(dev);
 
-       queue_work(dev->wq, &dev->poll_work);
+       queue_delayed_work(dev->wq, &dev->poll_work,
+                          msecs_to_jiffies(PN533_POLL_INTERVAL));
+}
+
+static int pn533_rf_complete(struct pn533 *dev, void *arg,
+                            struct sk_buff *resp)
+{
+       int rc = 0;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+
+               nfc_dev_err(&dev->interface->dev, "%s RF setting error %d",
+                           __func__, rc);
+
+               return rc;
+       }
+
+       queue_delayed_work(dev->wq, &dev->poll_work,
+                          msecs_to_jiffies(PN533_POLL_INTERVAL));
+
+       dev_kfree_skb(resp);
+       return rc;
+}
+
+static void pn533_wq_rf(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, rf_work);
+       struct sk_buff *skb;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       skb = pn533_alloc_skb(dev, 2);
+       if (!skb)
+               return;
+
+       *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD;
+       *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
+       rc = pn533_send_cmd_async(dev, PN533_CMD_RF_CONFIGURATION, skb,
+                                 pn533_rf_complete, NULL);
+       if (rc < 0) {
+               dev_kfree_skb(skb);
+               nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc);
+       }
+
+       return;
 }
 
 static int pn533_poll_complete(struct pn533 *dev, void *arg,
@@ -1705,7 +1818,8 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg,
        }
 
        pn533_poll_next_mod(dev);
-       queue_work(dev->wq, &dev->poll_work);
+       /* Not target found, turn radio off */
+       queue_work(dev->wq, &dev->rf_work);
 
 done:
        dev_kfree_skb(resp);
@@ -1770,7 +1884,7 @@ static int pn533_send_poll_frame(struct pn533 *dev)
 
 static void pn533_wq_poll(struct work_struct *work)
 {
-       struct pn533 *dev = container_of(work, struct pn533, poll_work);
+       struct pn533 *dev = container_of(work, struct pn533, poll_work.work);
        struct pn533_poll_modulations *cur_mod;
        int rc;
 
@@ -1799,6 +1913,7 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
                            u32 im_protocols, u32 tm_protocols)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       u8 rand_mod;
 
        nfc_dev_dbg(&dev->interface->dev,
                    "%s: im protocols 0x%x tm protocols 0x%x",
@@ -1822,11 +1937,15 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev,
                        tm_protocols = 0;
        }
 
-       dev->poll_mod_curr = 0;
        pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
        dev->poll_protocols = im_protocols;
        dev->listen_protocols = tm_protocols;
 
+       /* Do not always start polling from the same modulation */
+       get_random_bytes(&rand_mod, sizeof(rand_mod));
+       rand_mod %= dev->poll_mod_count;
+       dev->poll_mod_curr = rand_mod;
+
        return pn533_send_poll_frame(dev);
 }
 
@@ -1845,6 +1964,7 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
        }
 
        pn533_abort_cmd(dev, GFP_KERNEL);
+       flush_delayed_work(&dev->poll_work);
        pn533_poll_reset_mod_list(dev);
 }
 
@@ -2037,28 +2157,15 @@ error:
        return rc;
 }
 
-static int pn533_mod_to_baud(struct pn533 *dev)
-{
-       switch (dev->poll_mod_curr) {
-       case PN533_POLL_MOD_106KBPS_A:
-               return 0;
-       case PN533_POLL_MOD_212KBPS_FELICA:
-               return 1;
-       case PN533_POLL_MOD_424KBPS_FELICA:
-               return 2;
-       default:
-               return -EINVAL;
-       }
-}
-
+static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
 #define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                             u8 comm_mode, u8 *gb, size_t gb_len)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        struct sk_buff *skb;
-       int rc, baud, skb_len;
-       u8 *next, *arg;
+       int rc, skb_len;
+       u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
 
        u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
 
@@ -2076,41 +2183,39 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                return -EBUSY;
        }
 
-       baud = pn533_mod_to_baud(dev);
-       if (baud < 0) {
-               nfc_dev_err(&dev->interface->dev,
-                           "Invalid curr modulation %d", dev->poll_mod_curr);
-               return baud;
-       }
-
        skb_len = 3 + gb_len; /* ActPass + BR + Next */
-       if (comm_mode == NFC_COMM_PASSIVE)
-               skb_len += PASSIVE_DATA_LEN;
+       skb_len += PASSIVE_DATA_LEN;
 
-       if (target && target->nfcid2_len)
-               skb_len += NFC_NFCID3_MAXSIZE;
+       /* NFCID3 */
+       skb_len += NFC_NFCID3_MAXSIZE;
+       if (target && !target->nfcid2_len) {
+               nfcid3[0] = 0x1;
+               nfcid3[1] = 0xfe;
+               get_random_bytes(nfcid3 + 2, 6);
+       }
 
        skb = pn533_alloc_skb(dev, skb_len);
        if (!skb)
                return -ENOMEM;
 
        *skb_put(skb, 1) = !comm_mode;  /* ActPass */
-       *skb_put(skb, 1) = baud;  /* Baud rate */
+       *skb_put(skb, 1) = 0x02;  /* 424 kbps */
 
        next = skb_put(skb, 1);  /* Next */
        *next = 0;
 
-       if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
-               memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
-                      PASSIVE_DATA_LEN);
-               *next |= 1;
-       }
+       /* Copy passive data */
+       memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+       *next |= 1;
 
-       if (target && target->nfcid2_len) {
+       /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+       if (target && target->nfcid2_len)
                memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
                       target->nfcid2_len);
-               *next |= 2;
-       }
+       else
+               memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+                      NFC_NFCID3_MAXSIZE);
+       *next |= 2;
 
        if (gb != NULL && gb_len > 0) {
                memcpy(skb_put(skb, gb_len), gb, gb_len);
@@ -2127,6 +2232,8 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
 
        *arg = !comm_mode;
 
+       pn533_rf_field(dev->nfc_dev, 0);
+
        rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
                                  pn533_in_dep_link_up_complete, arg);
 
@@ -2232,7 +2339,15 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
 
        if (mi) {
                dev->cmd_complete_mi_arg = arg;
-               queue_work(dev->wq, &dev->mi_work);
+               queue_work(dev->wq, &dev->mi_rx_work);
+               return -EINPROGRESS;
+       }
+
+       /* Prepare for the next round */
+       if (skb_queue_len(&dev->fragment_skb) > 0) {
+               dev->cmd_complete_dep_arg = arg;
+               queue_work(dev->wq, &dev->mi_tx_work);
+
                return -EINPROGRESS;
        }
 
@@ -2253,6 +2368,50 @@ _error:
        return rc;
 }
 
+/* Split the Tx skb into small chunks */
+static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
+{
+       struct sk_buff *frag;
+       int  frag_size;
+
+       do {
+               /* Remaining size */
+               if (skb->len > PN533_CMD_DATAFRAME_MAXLEN)
+                       frag_size = PN533_CMD_DATAFRAME_MAXLEN;
+               else
+                       frag_size = skb->len;
+
+               /* Allocate and reserve */
+               frag = pn533_alloc_skb(dev, frag_size);
+               if (!frag) {
+                       skb_queue_purge(&dev->fragment_skb);
+                       break;
+               }
+
+               /* Reserve the TG/MI byte */
+               skb_reserve(frag, 1);
+
+               /* MI + TG */
+               if (frag_size  == PN533_CMD_DATAFRAME_MAXLEN)
+                       *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
+               else
+                       *skb_push(frag, sizeof(u8)) =  1; /* TG */
+
+               memcpy(skb_put(frag, frag_size), skb->data, frag_size);
+
+               /* Reduce the size of incoming buffer */
+               skb_pull(skb, frag_size);
+
+               /* Add this to skb_queue */
+               skb_queue_tail(&dev->fragment_skb, frag);
+
+       } while (skb->len > 0);
+
+       dev_kfree_skb(skb);
+
+       return skb_queue_len(&dev->fragment_skb);
+}
+
 static int pn533_transceive(struct nfc_dev *nfc_dev,
                            struct nfc_target *target, struct sk_buff *skb,
                            data_exchange_cb_t cb, void *cb_context)
@@ -2263,15 +2422,6 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
-               /* TODO: Implement support to multi-part data exchange */
-               nfc_dev_err(&dev->interface->dev,
-                           "Data length greater than the max allowed: %d",
-                           PN533_CMD_DATAEXCH_DATA_MAXLEN);
-               rc = -ENOSYS;
-               goto error;
-       }
-
        if (!dev->tgt_active_prot) {
                nfc_dev_err(&dev->interface->dev,
                            "Can't exchange data if there is no active target");
@@ -2299,7 +2449,20 @@ static int pn533_transceive(struct nfc_dev *nfc_dev,
                        break;
                }
        default:
-               *skb_push(skb, sizeof(u8)) =  1; /*TG*/
+               /* jumbo frame ? */
+               if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+                       rc = pn533_fill_fragment_skbs(dev, skb);
+                       if (rc <= 0)
+                               goto error;
+
+                       skb = skb_dequeue(&dev->fragment_skb);
+                       if (!skb) {
+                               rc = -EIO;
+                               goto error;
+                       }
+               } else {
+                       *skb_push(skb, sizeof(u8)) =  1; /* TG */
+               }
 
                rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
                                           skb, pn533_data_exchange_complete,
@@ -2370,7 +2533,7 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
-       struct pn533 *dev = container_of(work, struct pn533, mi_work);
+       struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
 
        struct sk_buff *skb;
        int rc;
@@ -2418,6 +2581,61 @@ error:
        queue_work(dev->wq, &dev->cmd_work);
 }
 
+static void pn533_wq_mi_send(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, mi_tx_work);
+       struct sk_buff *skb;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       /* Grab the first skb in the queue */
+       skb = skb_dequeue(&dev->fragment_skb);
+
+       if (skb == NULL) {      /* No more data */
+               /* Reset the queue for future use */
+               skb_queue_head_init(&dev->fragment_skb);
+               goto error;
+       }
+
+       switch (dev->device_type) {
+       case PN533_DEVICE_PASORI:
+               if (dev->tgt_active_prot != NFC_PROTO_FELICA) {
+                       rc = -EIO;
+                       break;
+               }
+
+               rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU,
+                                                skb,
+                                                pn533_data_exchange_complete,
+                                                dev->cmd_complete_dep_arg);
+
+               break;
+
+       default:
+               /* Still some fragments? */
+               rc = pn533_send_cmd_direct_async(dev,PN533_CMD_IN_DATA_EXCHANGE,
+                                                skb,
+                                                pn533_data_exchange_complete,
+                                                dev->cmd_complete_dep_arg);
+
+               break;
+       }
+
+       if (rc == 0) /* success */
+               return;
+
+       nfc_dev_err(&dev->interface->dev,
+                   "Error %d when trying to perform data_exchange", rc);
+
+       dev_kfree_skb(skb);
+       kfree(dev->cmd_complete_dep_arg);
+
+error:
+       pn533_send_ack(dev, GFP_KERNEL);
+       queue_work(dev->wq, &dev->cmd_work);
+}
+
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
                                                                u8 cfgdata_len)
 {
@@ -2562,6 +2780,8 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf)
        u8 rf_field = !!rf;
        int rc;
 
+       rf_field |= PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
        rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
                                     (u8 *)&rf_field, 1);
        if (rc) {
@@ -2605,17 +2825,6 @@ static int pn533_setup(struct pn533 *dev)
 
        switch (dev->device_type) {
        case PN533_DEVICE_STD:
-               max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
-               max_retries.mx_rty_psl = 2;
-               max_retries.mx_rty_passive_act =
-                       PN533_CONFIG_MAX_RETRIES_NO_RETRY;
-
-               timing.rfu = PN533_CONFIG_TIMING_102;
-               timing.atr_res_timeout = PN533_CONFIG_TIMING_204;
-               timing.dep_timeout = PN533_CONFIG_TIMING_409;
-
-               break;
-
        case PN533_DEVICE_PASORI:
        case PN533_DEVICE_ACR122U:
                max_retries.mx_rty_atr = 0x2;
@@ -2729,9 +2938,11 @@ static int pn533_probe(struct usb_interface *interface,
 
        INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
        INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
-       INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
+       INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
+       INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
        INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
-       INIT_WORK(&dev->poll_work, pn533_wq_poll);
+       INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
+       INIT_WORK(&dev->rf_work, pn533_wq_rf);
        dev->wq = alloc_ordered_workqueue("pn533", 0);
        if (dev->wq == NULL)
                goto error;
@@ -2741,6 +2952,7 @@ static int pn533_probe(struct usb_interface *interface,
        dev->listen_timer.function = pn533_listen_mode_timer;
 
        skb_queue_head_init(&dev->resp_q);
+       skb_queue_head_init(&dev->fragment_skb);
 
        INIT_LIST_HEAD(&dev->cmd_queue);
 
@@ -2842,6 +3054,7 @@ static void pn533_disconnect(struct usb_interface *interface)
        usb_kill_urb(dev->in_urb);
        usb_kill_urb(dev->out_urb);
 
+       flush_delayed_work(&dev->poll_work);
        destroy_workqueue(dev->wq);
 
        skb_queue_purge(&dev->resp_q);
index 8cf64c1..01e27d4 100644 (file)
 #include <linux/miscdevice.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/unaligned/access_ok.h>
 #include <linux/platform_data/pn544.h>
 
 #include <net/nfc/hci.h>
 #include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
 
 #include "pn544.h"
 
@@ -55,6 +58,58 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
 
 #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
 
+#define PN544_FW_CMD_WRITE 0x08
+#define PN544_FW_CMD_CHECK 0x06
+
+struct pn544_i2c_fw_frame_write {
+       u8 cmd;
+       u16 be_length;
+       u8 be_dest_addr[3];
+       u16 be_datalen;
+       u8 data[];
+} __packed;
+
+struct pn544_i2c_fw_frame_check {
+       u8 cmd;
+       u16 be_length;
+       u8 be_start_addr[3];
+       u16 be_datalen;
+       u16 be_crc;
+} __packed;
+
+struct pn544_i2c_fw_frame_response {
+       u8 status;
+       u16 be_length;
+} __packed;
+
+struct pn544_i2c_fw_blob {
+       u32 be_size;
+       u32 be_destaddr;
+       u8 data[];
+};
+
+#define PN544_FW_CMD_RESULT_TIMEOUT 0x01
+#define PN544_FW_CMD_RESULT_BAD_CRC 0x02
+#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08
+#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B
+#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11
+#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18
+#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
+
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+#define PN544_FW_WRITE_BUFFER_MAX_LEN 0x9f7
+#define PN544_FW_I2C_MAX_PAYLOAD PN544_HCI_I2C_LLC_MAX_SIZE
+#define PN544_FW_I2C_WRITE_FRAME_HEADER_LEN 8
+#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\
+                                        PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\
+                                        PN544_FW_WRITE_BUFFER_MAX_LEN)
+
+#define FW_WORK_STATE_IDLE 1
+#define FW_WORK_STATE_START 2
+#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3
+#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4
+
 struct pn544_i2c_phy {
        struct i2c_client *i2c_dev;
        struct nfc_hci_dev *hdev;
@@ -64,7 +119,18 @@ struct pn544_i2c_phy {
        unsigned int gpio_fw;
        unsigned int en_polarity;
 
+       struct work_struct fw_work;
+       int fw_work_state;
+       char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+       const struct firmware *fw;
+       u32 fw_blob_dest_addr;
+       size_t fw_blob_size;
+       const u8 *fw_blob_data;
+       size_t fw_written;
+       int fw_cmd_result;
+
        int powered;
+       int run_mode;
 
        int hard_fault;         /*
                                 * < 0 if hardware error occured (e.g. i2c err)
@@ -122,15 +188,22 @@ out:
        gpio_set_value(phy->gpio_en, !phy->en_polarity);
 }
 
+static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode)
+{
+       gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0);
+       gpio_set_value(phy->gpio_en, phy->en_polarity);
+       usleep_range(10000, 15000);
+
+       phy->run_mode = run_mode;
+}
+
 static int pn544_hci_i2c_enable(void *phy_id)
 {
        struct pn544_i2c_phy *phy = phy_id;
 
        pr_info(DRIVER_DESC ": %s\n", __func__);
 
-       gpio_set_value(phy->gpio_fw, 0);
-       gpio_set_value(phy->gpio_en, phy->en_polarity);
-       usleep_range(10000, 15000);
+       pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
 
        phy->powered = 1;
 
@@ -305,6 +378,42 @@ flush:
        return r;
 }
 
+static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
+{
+       int r;
+       struct pn544_i2c_fw_frame_response response;
+       struct i2c_client *client = phy->i2c_dev;
+
+       r = i2c_master_recv(client, (char *) &response, sizeof(response));
+       if (r != sizeof(response)) {
+               dev_err(&client->dev, "cannot read fw status\n");
+               return -EIO;
+       }
+
+       usleep_range(3000, 6000);
+
+       switch (response.status) {
+       case 0:
+               return 0;
+       case PN544_FW_CMD_RESULT_TIMEOUT:
+               return -ETIMEDOUT;
+       case PN544_FW_CMD_RESULT_BAD_CRC:
+               return -ENODATA;
+       case PN544_FW_CMD_RESULT_ACCESS_DENIED:
+               return -EACCES;
+       case PN544_FW_CMD_RESULT_PROTOCOL_ERROR:
+               return -EPROTO;
+       case PN544_FW_CMD_RESULT_INVALID_PARAMETER:
+               return -EINVAL;
+       case PN544_FW_CMD_RESULT_INVALID_LENGTH:
+               return -EBADMSG;
+       case PN544_FW_CMD_RESULT_WRITE_FAILED:
+               return -EIO;
+       default:
+               return -EIO;
+       }
+}
+
 /*
  * Reads an shdlc frame from the chip. This is not as straightforward as it
  * seems. There are cases where we could loose the frame start synchronization.
@@ -339,19 +448,23 @@ static irqreturn_t pn544_hci_i2c_irq_thread_fn(int irq, void *phy_id)
        if (phy->hard_fault != 0)
                return IRQ_HANDLED;
 
-       r = pn544_hci_i2c_read(phy, &skb);
-       if (r == -EREMOTEIO) {
-               phy->hard_fault = r;
+       if (phy->run_mode == PN544_FW_MODE) {
+               phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy);
+               schedule_work(&phy->fw_work);
+       } else {
+               r = pn544_hci_i2c_read(phy, &skb);
+               if (r == -EREMOTEIO) {
+                       phy->hard_fault = r;
 
-               nfc_hci_recv_frame(phy->hdev, NULL);
+                       nfc_hci_recv_frame(phy->hdev, NULL);
 
-               return IRQ_HANDLED;
-       } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
-               return IRQ_HANDLED;
-       }
-
-       nfc_hci_recv_frame(phy->hdev, skb);
+                       return IRQ_HANDLED;
+               } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+                       return IRQ_HANDLED;
+               }
 
+               nfc_hci_recv_frame(phy->hdev, skb);
+       }
        return IRQ_HANDLED;
 }
 
@@ -361,6 +474,215 @@ static struct nfc_phy_ops i2c_phy_ops = {
        .disable = pn544_hci_i2c_disable,
 };
 
+static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
+{
+       struct pn544_i2c_phy *phy = phy_id;
+
+       pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n",
+               firmware_name);
+
+       strcpy(phy->firmware_name, firmware_name);
+
+       phy->fw_work_state = FW_WORK_STATE_START;
+
+       schedule_work(&phy->fw_work);
+
+       return 0;
+}
+
+static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy,
+                                          int result)
+{
+       pr_info(DRIVER_DESC ": Firmware Download Complete, result=%d\n", result);
+
+       pn544_hci_i2c_disable(phy);
+
+       phy->fw_work_state = FW_WORK_STATE_IDLE;
+
+       if (phy->fw) {
+               release_firmware(phy->fw);
+               phy->fw = NULL;
+       }
+
+       nfc_fw_download_done(phy->hdev->ndev, phy->firmware_name, (u32) -result);
+}
+
+static int pn544_hci_i2c_fw_write_cmd(struct i2c_client *client, u32 dest_addr,
+                                     const u8 *data, u16 datalen)
+{
+       u8 frame[PN544_FW_I2C_MAX_PAYLOAD];
+       struct pn544_i2c_fw_frame_write *framep;
+       u16 params_len;
+       int framelen;
+       int r;
+
+       if (datalen > PN544_FW_I2C_WRITE_DATA_MAX_LEN)
+               datalen = PN544_FW_I2C_WRITE_DATA_MAX_LEN;
+
+       framep = (struct pn544_i2c_fw_frame_write *) frame;
+
+       params_len = sizeof(framep->be_dest_addr) +
+                    sizeof(framep->be_datalen) + datalen;
+       framelen = params_len + sizeof(framep->cmd) +
+                            sizeof(framep->be_length);
+
+       framep->cmd = PN544_FW_CMD_WRITE;
+
+       put_unaligned_be16(params_len, &framep->be_length);
+
+       framep->be_dest_addr[0] = (dest_addr & 0xff0000) >> 16;
+       framep->be_dest_addr[1] = (dest_addr & 0xff00) >> 8;
+       framep->be_dest_addr[2] = dest_addr & 0xff;
+
+       put_unaligned_be16(datalen, &framep->be_datalen);
+
+       memcpy(framep->data, data, datalen);
+
+       r = i2c_master_send(client, frame, framelen);
+
+       if (r == framelen)
+               return datalen;
+       else if (r < 0)
+               return r;
+       else
+               return -EIO;
+}
+
+static int pn544_hci_i2c_fw_check_cmd(struct i2c_client *client, u32 start_addr,
+                                     const u8 *data, u16 datalen)
+{
+       struct pn544_i2c_fw_frame_check frame;
+       int r;
+       u16 crc;
+
+       /* calculate local crc for the data we want to check */
+       crc = crc_ccitt(0xffff, data, datalen);
+
+       frame.cmd = PN544_FW_CMD_CHECK;
+
+       put_unaligned_be16(sizeof(frame.be_start_addr) +
+                          sizeof(frame.be_datalen) + sizeof(frame.be_crc),
+                          &frame.be_length);
+
+       /* tell the chip the memory region to which our crc applies */
+       frame.be_start_addr[0] = (start_addr & 0xff0000) >> 16;
+       frame.be_start_addr[1] = (start_addr & 0xff00) >> 8;
+       frame.be_start_addr[2] = start_addr & 0xff;
+
+       put_unaligned_be16(datalen, &frame.be_datalen);
+
+       /*
+        * and give our local crc. Chip will calculate its own crc for the
+        * region and compare with ours.
+        */
+       put_unaligned_be16(crc, &frame.be_crc);
+
+       r = i2c_master_send(client, (const char *) &frame, sizeof(frame));
+
+       if (r == sizeof(frame))
+               return 0;
+       else if (r < 0)
+               return r;
+       else
+               return -EIO;
+}
+
+static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy)
+{
+       int r;
+
+       r = pn544_hci_i2c_fw_write_cmd(phy->i2c_dev,
+                                      phy->fw_blob_dest_addr + phy->fw_written,
+                                      phy->fw_blob_data + phy->fw_written,
+                                      phy->fw_blob_size - phy->fw_written);
+       if (r < 0)
+               return r;
+
+       phy->fw_written += r;
+       phy->fw_work_state = FW_WORK_STATE_WAIT_WRITE_ANSWER;
+
+       return 0;
+}
+
+static void pn544_hci_i2c_fw_work(struct work_struct *work)
+{
+       struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
+                                               fw_work);
+       int r;
+       struct pn544_i2c_fw_blob *blob;
+
+       switch (phy->fw_work_state) {
+       case FW_WORK_STATE_START:
+               pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
+
+               r = request_firmware(&phy->fw, phy->firmware_name,
+                                    &phy->i2c_dev->dev);
+               if (r < 0)
+                       goto exit_state_start;
+
+               blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
+               phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+               phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr);
+               phy->fw_blob_data = blob->data;
+
+               phy->fw_written = 0;
+               r = pn544_hci_i2c_fw_write_chunk(phy);
+
+exit_state_start:
+               if (r < 0)
+                       pn544_hci_i2c_fw_work_complete(phy, r);
+               break;
+
+       case FW_WORK_STATE_WAIT_WRITE_ANSWER:
+               r = phy->fw_cmd_result;
+               if (r < 0)
+                       goto exit_state_wait_write_answer;
+
+               if (phy->fw_written == phy->fw_blob_size) {
+                       r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev,
+                                                      phy->fw_blob_dest_addr,
+                                                      phy->fw_blob_data,
+                                                      phy->fw_blob_size);
+                       if (r < 0)
+                               goto exit_state_wait_write_answer;
+                       phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER;
+                       break;
+               }
+
+               r = pn544_hci_i2c_fw_write_chunk(phy);
+
+exit_state_wait_write_answer:
+               if (r < 0)
+                       pn544_hci_i2c_fw_work_complete(phy, r);
+               break;
+
+       case FW_WORK_STATE_WAIT_CHECK_ANSWER:
+               r = phy->fw_cmd_result;
+               if (r < 0)
+                       goto exit_state_wait_check_answer;
+
+               blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data +
+                      phy->fw_blob_size);
+               phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+               if (phy->fw_blob_size != 0) {
+                       phy->fw_blob_dest_addr =
+                                       get_unaligned_be32(&blob->be_destaddr);
+                       phy->fw_blob_data = blob->data;
+
+                       phy->fw_written = 0;
+                       r = pn544_hci_i2c_fw_write_chunk(phy);
+               }
+
+exit_state_wait_check_answer:
+               if (r < 0 || phy->fw_blob_size == 0)
+                       pn544_hci_i2c_fw_work_complete(phy, r);
+               break;
+
+       default:
+               break;
+       }
+}
+
 static int pn544_hci_i2c_probe(struct i2c_client *client,
                               const struct i2c_device_id *id)
 {
@@ -384,6 +706,9 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
                return -ENOMEM;
        }
 
+       INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
+       phy->fw_work_state = FW_WORK_STATE_IDLE;
+
        phy->i2c_dev = client;
        i2c_set_clientdata(client, phy);
 
@@ -420,7 +745,8 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
 
        r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
                            PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM,
-                           PN544_HCI_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
+                           PN544_HCI_I2C_LLC_MAX_PAYLOAD,
+                           pn544_hci_i2c_fw_download, &phy->hdev);
        if (r < 0)
                goto err_hci;
 
@@ -443,6 +769,10 @@ static int pn544_hci_i2c_remove(struct i2c_client *client)
 
        dev_dbg(&client->dev, "%s\n", __func__);
 
+       cancel_work_sync(&phy->fw_work);
+       if (phy->fw_work_state != FW_WORK_STATE_IDLE)
+               pn544_hci_i2c_fw_work_complete(phy, -ENODEV);
+
        pn544_hci_remove(phy->hdev);
 
        if (phy->powered)
index b5d3d18..ee67de5 100644 (file)
@@ -45,7 +45,7 @@ static int pn544_mei_probe(struct mei_cl_device *device,
 
        r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
                            MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
-                           &phy->hdev);
+                           NULL, &phy->hdev);
        if (r < 0) {
                nfc_mei_phy_free(phy);
 
index 0d17da7..078e62f 100644 (file)
@@ -31,9 +31,6 @@
 /* Timing restrictions (ms) */
 #define PN544_HCI_RESETVEN_TIME                30
 
-#define HCI_MODE 0
-#define FW_MODE 1
-
 enum pn544_state {
        PN544_ST_COLD,
        PN544_ST_FW_READY,
@@ -130,6 +127,8 @@ struct pn544_hci_info {
        int async_cb_type;
        data_exchange_cb_t async_cb;
        void *async_cb_context;
+
+       fw_download_t fw_download;
 };
 
 static int pn544_hci_open(struct nfc_hci_dev *hdev)
@@ -782,6 +781,17 @@ exit:
        return r;
 }
 
+static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
+                                const char *firmware_name)
+{
+       struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       if (info->fw_download == NULL)
+               return -ENOTSUPP;
+
+       return info->fw_download(info->phy_id, firmware_name);
+}
+
 static struct nfc_hci_ops pn544_hci_ops = {
        .open = pn544_hci_open,
        .close = pn544_hci_close,
@@ -796,11 +806,12 @@ static struct nfc_hci_ops pn544_hci_ops = {
        .tm_send = pn544_hci_tm_send,
        .check_presence = pn544_hci_check_presence,
        .event_received = pn544_hci_event_received,
+       .fw_download = pn544_hci_fw_download,
 };
 
 int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    int phy_headroom, int phy_tailroom, int phy_payload,
-                   struct nfc_hci_dev **hdev)
+                   fw_download_t fw_download, struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
        u32 protocols;
@@ -816,6 +827,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 
        info->phy_ops = phy_ops;
        info->phy_id = phy_id;
+       info->fw_download = fw_download;
        info->state = PN544_ST_COLD;
        mutex_init(&info->info_lock);
 
index f47c645..01020e5 100644 (file)
 
 #define DRIVER_DESC "HCI NFC driver for PN544"
 
+#define PN544_HCI_MODE 0
+#define PN544_FW_MODE 1
+
+typedef int (*fw_download_t)(void *context, const char *firmware_name);
+
 int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    int phy_headroom, int phy_tailroom, int phy_payload,
-                   struct nfc_hci_dev **hdev);
+                   fw_download_t fw_download, struct nfc_hci_dev **hdev);
 void pn544_hci_remove(struct nfc_hci_dev *hdev);
 
 #endif /* __LOCAL_PN544_H_ */
index 36171fd..2cd9b0e 100644 (file)
@@ -138,7 +138,7 @@ config SSB_DRIVER_MIPS
 
 config SSB_SFLASH
        bool "SSB serial flash support"
-       depends on SSB_DRIVER_MIPS && BROKEN
+       depends on SSB_DRIVER_MIPS
        default y
 
 # Assumption: We are on embedded, if we compile the MIPS core.
index e84cf04..50328de 100644 (file)
@@ -151,8 +151,8 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
        sflash->size = sflash->blocksize * sflash->numblocks;
        sflash->present = true;
 
-       pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
-               e->name, e->blocksize, e->numblocks);
+       pr_info("Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
+               e->name, sflash->size / 1024, e->blocksize, e->numblocks);
 
        /* Prepare platform device, but don't register it yet. It's too early,
         * malloc (required by device_private_init) is not available yet. */
@@ -160,7 +160,5 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
                                         sflash->size;
        ssb_sflash_dev.dev.platform_data = sflash;
 
-       pr_err("Serial flash support is not implemented yet!\n");
-
-       return -ENOTSUPP;
+       return 0;
 }
index 8fc9f58..7f01549 100644 (file)
@@ -14,7 +14,7 @@
  * Copyright (c) 2004, Intel Corporation
  *
  * Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <andrea.merello@gmail.com>
  *
  * 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
index d5df0d6..10b2210 100644 (file)
@@ -14,7 +14,7 @@
  ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index 00f9af0..b65db54 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index d9add53..e528206 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index 89ed86e..b346653 100644 (file)
@@ -25,7 +25,7 @@
 ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index edacc80..d052f4a 100644 (file)
@@ -1,6 +1,6 @@
 /*
    This is part of rtl8180 OpenSource driver.
-   Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+   Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
    Released under the terms of GPL (General Public Licence)
 
    Parts of this driver are based on the GPL part of the
index 79e7391..b52b5b0 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8180 OpenSource driver
-       Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the official realtek driver
index ca69155..fd2bfea 100644 (file)
@@ -1,6 +1,6 @@
 /*
    This is part of rtl818x pci OpenSource driver - v 0.1
-   Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+   Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
    Released under the terms of GPL (General Public License)
 
    Parts of this driver are based on the GPL part of the official
@@ -70,7 +70,7 @@ static int hwwep;
 
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(pci, rtl8180_pci_id_tbl);
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
 MODULE_DESCRIPTION("Linux driver for Realtek RTL8187SE WiFi cards");
 
 module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
index 5339381..92c05af 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8180 OpenSource driver.
-       Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the
index c6f2128..c94ca07 100644 (file)
@@ -1,7 +1,7 @@
 /*
        This is part of the rtl8180-sa2400 driver
        released under the GPL (See file COPYING for details).
-       Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+       Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
 
        This files contains programming code for the rtl8225
        radio frontend.
index c592f79..9ae96b7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is part of the rtl8180-sa2400 driver
  * released under the GPL (See file COPYING for details).
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * This files contains programming code for the rtl8225
  * radio frontend.
index 156b758..dab7875 100644 (file)
@@ -2,7 +2,7 @@
        This file contains wireless extension handlers.
 
        This is part of rtl8180 OpenSource driver.
-       Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part
index 4081914..d471520 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8180 OpenSource driver - v 0.3
-       Copyright (C) Andrea Merello 2004  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the official realtek driver
index 50c7bb7..74fbd70 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index b9b3b52..dbe0e1c 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index baf3b63..fa5603a 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index fa607f9..7d075d3 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 2b6c61c..8d45c8d 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 87d4d34..9de1dc3 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index c1ccff4..a6778e0 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 9452e16..adea2b4 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 0cfb3ec..529ea54 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 5abbee3..2ad92ee 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 28c7da6..356aec4 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index c9a7c56..a8c2ade 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index df79d6c..962f2e5 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 3485ef1..05ef49f 100644 (file)
@@ -14,7 +14,7 @@
  * Copyright (c) 2004, Intel Corporation
  *
  * Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <andrea.merello@gmail.com>
  *
  * 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
index 2bfc115..c59f67b 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
  *
  * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index e75364e..96aa3a2 100644 (file)
@@ -14,7 +14,7 @@
  ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index aefffac..0cbf6f5 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index 740cf85..e6af8cf 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index 759d7c7..1cc6a9d 100644 (file)
@@ -25,7 +25,7 @@
 ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index b08bbae..0fab112 100644 (file)
@@ -1 +1 @@
-Andrea Merello <andreamrl@tiscali.it>
+Andrea Merello <andrea.merello@gmail.com>
index c9f3bb3..bc64f05 100644 (file)
@@ -14,7 +14,7 @@
  * Copyright (c) 2004, Intel Corporation
  *
  * Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <andrea.merello@gmail.com>
  *
  * 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
index a6b1840..59900bf 100644 (file)
@@ -14,7 +14,7 @@
  ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index 8a0075d..5fd6969 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index 60746b8..7b7d929 100644 (file)
@@ -1,5 +1,5 @@
 /* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
  *
  * Mostly extracted from the rtl8180-sa2400 driver for the
  * in-kernel generic ieee802.11 stack.
index 9955042..a7bcc64 100644 (file)
@@ -25,7 +25,7 @@
 ******************************************************************************
 
   Few modifications for Realtek's Wi-Fi drivers by
-  Andrea Merello <andreamrl@tiscali.it>
+  Andrea Merello <andrea.merello@gmail.com>
 
   A special thanks goes to Realtek for their support !
 
index d219998..c61729b 100644 (file)
@@ -3,7 +3,7 @@
    memory is addressed by 16 bits words.
 
    This is part of rtl8180 OpenSource driver.
-   Copyright (C) Andrea Merello 2004  <andreamrl@tiscali.it>
+   Copyright (C) Andrea Merello 2004  <andrea.merello@gmail.com>
    Released under the terms of GPL (General Public Licence)
 
    Parts of this driver are based on the GPL part of the
index 5cea51e..ee55dbf 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8187 OpenSource driver
-       Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the official realtek driver
index 0c58d0e..999968d 100644 (file)
@@ -5,7 +5,7 @@
    does not do anything useful.
 
    This is part of rtl8180 OpenSource driver.
-   Copyright (C) Andrea Merello 2004  <andreamrl@tiscali.it>
+   Copyright (C) Andrea Merello 2004  <andrea.merello@gmail.com>
    Released under the terms of GPL (General Public Licence)
 */
 
index 52d6fba..4be63da 100644 (file)
@@ -5,7 +5,7 @@
        does not do anything useful.
 
        This is part of rtl8180 OpenSource driver.
-       Copyright (C) Andrea Merello 2004  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
 */
index b64dd66..592e780 100644 (file)
@@ -1,7 +1,7 @@
 /*
   This is part of the rtl8180-sa2400 driver
   released under the GPL (See file COPYING for details).
-  Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+  Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
 
   This files contains programming code for the rtl8256
   radio frontend.
index 338e7bc..b484ee1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This is part of rtl8187 OpenSource driver.
- * Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+ * Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
  * Released under the terms of GPL (General Public Licence)
  *
  * Parts of this driver are based on the GPL part of the
index 14c14c2..cd0946d 100644 (file)
@@ -3,7 +3,7 @@
  * Linux device driver for RTL8192U
  *
  * Based on the r8187 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
  * 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.
index 7e612aa..dd07a73 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8187 OpenSource driver.
-       Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the
index 3e25763..61f6620 100644 (file)
@@ -2,7 +2,7 @@
    This file contains wireless extension handlers.
 
    This is part of rtl8180 OpenSource driver.
-   Copyright (C) Andrea Merello 2004-2005  <andreamrl@tiscali.it>
+   Copyright (C) Andrea Merello 2004-2005  <andrea.merello@gmail.com>
    Released under the terms of GPL (General Public Licence)
 
    Parts of this driver are based on the GPL part
index 9f6b105..ae7a617 100644 (file)
@@ -1,6 +1,6 @@
 /*
        This is part of rtl8180 OpenSource driver - v 0.3
-       Copyright (C) Andrea Merello 2004  <andreamrl@tiscali.it>
+       Copyright (C) Andrea Merello 2004  <andrea.merello@gmail.com>
        Released under the terms of GPL (General Public Licence)
 
        Parts of this driver are based on the GPL part of the official realtek driver
index 622fc50..4d043c3 100644 (file)
@@ -72,7 +72,19 @@ struct bcma_host_ops {
 /* Core-ID values. */
 #define BCMA_CORE_OOB_ROUTER           0x367   /* Out of band */
 #define BCMA_CORE_4706_CHIPCOMMON      0x500
+#define BCMA_CORE_PCIEG2               0x501
+#define BCMA_CORE_DMA                  0x502
+#define BCMA_CORE_SDIO3                        0x503
+#define BCMA_CORE_USB20                        0x504
+#define BCMA_CORE_USB30                        0x505
+#define BCMA_CORE_A9JTAG               0x506
+#define BCMA_CORE_DDR23                        0x507
+#define BCMA_CORE_ROM                  0x508
+#define BCMA_CORE_NAND                 0x509
+#define BCMA_CORE_QSPI                 0x50A
+#define BCMA_CORE_CHIPCOMMON_B         0x50B
 #define BCMA_CORE_4706_SOC_RAM         0x50E
+#define BCMA_CORE_ARMCA9               0x510
 #define BCMA_CORE_4706_MAC_GBIT                0x52D
 #define BCMA_CORE_AMEMC                        0x52E   /* DDR1/2 memory controller core */
 #define BCMA_CORE_ALTA                 0x534   /* I2S core */
@@ -177,6 +189,11 @@ struct bcma_host_ops {
 #define  BCMA_PKG_ID_BCM5357   11
 #define BCMA_CHIP_ID_BCM53572  53572
 #define  BCMA_PKG_ID_BCM47188  9
+#define BCMA_CHIP_ID_BCM4707   53010
+#define  BCMA_PKG_ID_BCM4707   1
+#define  BCMA_PKG_ID_BCM4708   2
+#define  BCMA_PKG_ID_BCM4709   0
+#define BCMA_CHIP_ID_BCM53018  53018
 
 /* Board types (on PCI usually equals to the subsystem dev id) */
 /* BCM4313 */
index 424760f..d66033f 100644 (file)
@@ -181,10 +181,31 @@ struct pci_dev;
 
 #define BCMA_CORE_PCI_CFG_DEVCTRL              0xd8
 
+#define BCMA_CORE_PCI_
+
+/* MDIO devices (SERDES modules) */
+#define BCMA_CORE_PCI_MDIO_IEEE0               0x000
+#define BCMA_CORE_PCI_MDIO_IEEE1               0x001
+#define BCMA_CORE_PCI_MDIO_BLK0                        0x800
+#define BCMA_CORE_PCI_MDIO_BLK1                        0x801
+#define  BCMA_CORE_PCI_MDIO_BLK1_MGMT0         0x16
+#define  BCMA_CORE_PCI_MDIO_BLK1_MGMT1         0x17
+#define  BCMA_CORE_PCI_MDIO_BLK1_MGMT2         0x18
+#define  BCMA_CORE_PCI_MDIO_BLK1_MGMT3         0x19
+#define  BCMA_CORE_PCI_MDIO_BLK1_MGMT4         0x1A
+#define BCMA_CORE_PCI_MDIO_BLK2                        0x802
+#define BCMA_CORE_PCI_MDIO_BLK3                        0x803
+#define BCMA_CORE_PCI_MDIO_BLK4                        0x804
+#define BCMA_CORE_PCI_MDIO_TXPLL               0x808   /* TXPLL register block idx */
+#define BCMA_CORE_PCI_MDIO_TXCTRL0             0x820
+#define BCMA_CORE_PCI_MDIO_SERDESID            0x831
+#define BCMA_CORE_PCI_MDIO_RXCTRL0             0x840
+
 /* PCIE Root Capability Register bits (Host mode only) */
 #define BCMA_CORE_PCI_RC_CRS_VISIBILITY                0x0001
 
 struct bcma_drv_pci;
+struct bcma_bus;
 
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 struct bcma_drv_pci_host {
@@ -219,7 +240,8 @@ struct bcma_drv_pci {
 extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
 extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
                                 struct bcma_device *core, bool enable);
-extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend);
+extern void bcma_core_pci_up(struct bcma_bus *bus);
+extern void bcma_core_pci_down(struct bcma_bus *bus);
 
 extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
 extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
index b0dc87a..23a8877 100644 (file)
@@ -1709,6 +1709,10 @@ enum ieee80211_eid {
        WLAN_EID_OPMODE_NOTIF = 199,
        WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
        WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+       WLAN_EID_EXTENDED_BSS_LOAD = 193,
+       WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+       WLAN_EID_AID = 197,
+       WLAN_EID_QUIET_CHANNEL = 198,
 
        /* 802.11ad */
        WLAN_EID_NON_TX_BSSID_CAP =  83,
@@ -1860,6 +1864,11 @@ enum ieee80211_tdls_actioncode {
        WLAN_TDLS_DISCOVERY_REQUEST = 10,
 };
 
+/* Interworking capabilities are set in 7th bit of 4th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED    BIT(7)
+
 /*
  * TDLS capabililites to be enabled in the 5th byte of the
  * @WLAN_EID_EXT_CAPABILITY information element
@@ -2279,4 +2288,8 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
        return !!(tim->virtual_map[index] & mask);
 }
 
+/* convert time units */
+#define TU_TO_JIFFIES(x)       (usecs_to_jiffies((x) * 1024))
+#define TU_TO_EXP_TIME(x)      (jiffies + TU_TO_JIFFIES(x))
+
 #endif /* LINUX_IEEE80211_H */
index b717499..e75dcbf 100644 (file)
@@ -94,6 +94,10 @@ void __init brcmfmac_init_pdata(void)
  * Set this to true if the SDIO host controller has higher align requirement
  * than 32 bytes for each scatterlist item.
  *
+ * sd_head_align: alignment requirement for start of data buffer
+ *
+ * sd_sgentry_align: length alignment requirement for each sg entry
+ *
  * power_on: This function is called by the brcmfmac when the module gets
  * loaded. This can be particularly useful for low power devices. The platform
  * spcific routine may for example decide to power up the complete device.
@@ -121,6 +125,8 @@ struct brcmfmac_sdio_platform_data {
        unsigned int oob_irq_nr;
        unsigned long oob_irq_flags;
        bool broken_sg_support;
+       unsigned short sd_head_align;
+       unsigned short sd_sgentry_align;
        void (*power_on)(void);
        void (*power_off)(void);
        void (*reset)(void);
index 10eb9b3..10d43d8 100644 (file)
@@ -107,6 +107,14 @@ struct bt_power {
  */
 #define BT_CHANNEL_POLICY_AMP_PREFERRED                2
 
+#define BT_VOICE               11
+struct bt_voice {
+       __u16 setting;
+};
+
+#define BT_VOICE_TRANSPARENT                   0x0003
+#define BT_VOICE_CVSD_16BIT                    0x0060
+
 __printf(1, 2)
 int bt_info(const char *fmt, ...);
 __printf(1, 2)
index 3c592cf..aaeaf09 100644 (file)
@@ -238,6 +238,7 @@ enum {
 #define LMP_CVSD       0x01
 #define LMP_PSCHEME    0x02
 #define LMP_PCONTROL   0x04
+#define LMP_TRANSPARENT        0x08
 
 #define LMP_RSSI_INQ   0x40
 #define LMP_ESCO       0x80
@@ -296,6 +297,12 @@ enum {
 #define HCI_AT_GENERAL_BONDING         0x04
 #define HCI_AT_GENERAL_BONDING_MITM    0x05
 
+/* I/O capabilities */
+#define HCI_IO_DISPLAY_ONLY    0x00
+#define HCI_IO_DISPLAY_YESNO   0x01
+#define HCI_IO_KEYBOARD_ONLY   0x02
+#define HCI_IO_NO_INPUT_OUTPUT 0x03
+
 /* Link Key types */
 #define HCI_LK_COMBINATION             0x00
 #define HCI_LK_LOCAL_UNIT              0x01
index f77885e..3ede820 100644 (file)
@@ -320,6 +320,7 @@ struct hci_conn {
        __u32           passkey_notify;
        __u8            passkey_entered;
        __u16           disc_timeout;
+       __u16           setting;
        unsigned long   flags;
 
        __u8            remote_cap;
@@ -569,7 +570,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
 }
 
 void hci_disconnect(struct hci_conn *conn, __u8 reason);
-void hci_setup_sync(struct hci_conn *conn, __u16 handle);
+bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
 void hci_sco_setup(struct hci_conn *conn, __u8 status);
 
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
@@ -584,6 +585,8 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
 
 struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
                             __u8 dst_type, __u8 sec_level, __u8 auth_type);
+struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
+                                __u16 setting);
 int hci_conn_check_link_mode(struct hci_conn *conn);
 int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
 int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
@@ -797,6 +800,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_lsto_capable(dev)      ((dev)->features[0][7] & LMP_LSTO)
 #define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR)
 #define lmp_ext_feat_capable(dev)  ((dev)->features[0][7] & LMP_EXTFEATURES)
+#define lmp_transp_capable(dev)    ((dev)->features[0][2] & LMP_TRANSPARENT)
 
 /* ----- Extended LMP capabilities ----- */
 #define lmp_host_ssp_capable(dev)  ((dev)->features[1][0] & LMP_HOST_SSP)
@@ -1213,4 +1217,8 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
 
 u8 bdaddr_to_le(u8 bdaddr_type);
 
+#define SCO_AIRMODE_MASK       0x0003
+#define SCO_AIRMODE_CVSD       0x0000
+#define SCO_AIRMODE_TRANSP     0x0003
+
 #endif /* __HCI_CORE_H */
index 1e35c43..e252a31 100644 (file)
@@ -73,6 +73,7 @@ struct sco_conn {
 struct sco_pinfo {
        struct bt_sock  bt;
        __u32           flags;
+       __u16           setting;
        struct sco_conn *conn;
 };
 
index 7b0730a..9ab7a06 100644 (file)
@@ -461,6 +461,33 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef)
 }
 
 /**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return min(chandef->chan->max_reg_power - 6,
+                          chandef->chan->max_power);
+       case NL80211_CHAN_WIDTH_10:
+               return min(chandef->chan->max_reg_power - 3,
+                          chandef->chan->max_power);
+       default:
+               break;
+       }
+       return chandef->chan->max_power;
+}
+
+/**
  * enum survey_info_flags - survey information flags
  *
  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -490,7 +517,7 @@ enum survey_info_flags {
  * @channel: the channel this survey record reports, mandatory
  * @filled: bitflag of flags from &enum survey_info_flags
  * @noise: channel noise in dBm. This and all following fields are
- *     optional
+ *     optional
  * @channel_time: amount of time in ms the radio spent on the channel
  * @channel_time_busy: amount of time the primary channel was sensed busy
  * @channel_time_ext_busy: amount of time the extension channel was sensed busy
@@ -546,9 +573,9 @@ struct cfg80211_crypto_settings {
 /**
  * struct cfg80211_beacon_data - beacon data
  * @head: head portion of beacon (before TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @tail: tail portion of beacon (after TIM IE)
- *     or %NULL if not changed
+ *     or %NULL if not changed
  * @head_len: length of @head
  * @tail_len: length of @tail
  * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
@@ -639,6 +666,30 @@ struct cfg80211_ap_settings {
 };
 
 /**
+ * struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offset_beacon: offset for the counter within the beacon (tail)
+ * @counter_offset_presp: offset for the counter within the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+       struct cfg80211_chan_def chandef;
+       struct cfg80211_beacon_data beacon_csa;
+       u16 counter_offset_beacon, counter_offset_presp;
+       struct cfg80211_beacon_data beacon_after;
+       bool radar_required;
+       bool block_tx;
+       u8 count;
+};
+
+/**
  * enum station_parameters_apply_mask - station parameter values to apply
  * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
  * @STATION_PARAM_APPLY_CAPABILITY: apply new capability
@@ -764,7 +815,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_PLINK_STATE: @plink_state filled
  * @STATION_INFO_SIGNAL: @signal filled
  * @STATION_INFO_TX_BITRATE: @txrate fields are filled
- *  (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
+ *     (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
  * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
  * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
  * @STATION_INFO_TX_RETRIES: @tx_retries filled
@@ -1285,6 +1336,7 @@ struct cfg80211_ssid {
  * @n_ssids: number of SSIDs
  * @channels: channels to scan on.
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
  * @flags: bit field of flags controlling operation
@@ -1300,6 +1352,7 @@ struct cfg80211_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        const u8 *ie;
        size_t ie_len;
        u32 flags;
@@ -1333,6 +1386,7 @@ struct cfg80211_match_set {
  * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
  * @n_ssids: number of SSIDs
  * @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
  * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
@@ -1352,6 +1406,7 @@ struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
+       enum nl80211_bss_scan_width scan_width;
        u32 interval;
        const u8 *ie;
        size_t ie_len;
@@ -1403,6 +1458,7 @@ struct cfg80211_bss_ies {
  * for use in scan results and similar.
  *
  * @channel: channel this BSS is on
+ * @scan_width: width of the control channel
  * @bssid: BSSID of the BSS
  * @beacon_interval: the beacon interval as from the frame
  * @capability: the capability field in host byte order
@@ -1424,6 +1480,7 @@ struct cfg80211_bss_ies {
  */
 struct cfg80211_bss {
        struct ieee80211_channel *channel;
+       enum nl80211_bss_scan_width scan_width;
 
        const struct cfg80211_bss_ies __rcu *ies;
        const struct cfg80211_bss_ies __rcu *beacon_ies;
@@ -1509,7 +1566,7 @@ enum cfg80211_assoc_req_flags {
  * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa: VHT capability override
  * @vht_capa_mask: VHT capability mask indicating which fields to use
@@ -1592,6 +1649,9 @@ struct cfg80211_disassoc_request {
  *     user space. Otherwise, port is marked authorized by default.
  * @basic_rates: bitmap of basic rates to use when creating the IBSS
  * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
+ *     will be used in ht_capa.  Un-supported values will be ignored.
+ * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
        u8 *ssid;
@@ -1605,6 +1665,8 @@ struct cfg80211_ibss_params {
        bool privacy;
        bool control_port;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       struct ieee80211_ht_cap ht_capa;
+       struct ieee80211_ht_cap ht_capa_mask;
 };
 
 /**
@@ -1630,9 +1692,9 @@ struct cfg80211_ibss_params {
  * @key: WEP key for shared key authentication
  * @flags:  See &enum cfg80211_assoc_req_flags
  * @bg_scan_period:  Background scan period in seconds
- *   or -1 to indicate that default value is to be used.
+ *     or -1 to indicate that default value is to be used.
  * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
- *   will be used in ht_capa.  Un-supported values will be ignored.
+ *     will be used in ht_capa.  Un-supported values will be ignored.
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  * @vht_capa:  VHT Capability overrides
  * @vht_capa_mask: The bits of vht_capa which are to be used.
@@ -1698,7 +1760,7 @@ struct cfg80211_pmksa {
 };
 
 /**
- * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern
+ * struct cfg80211_pkt_pattern - packet pattern
  * @mask: bitmask where to match pattern and where to ignore bytes,
  *     one bit per byte, in same format as nl80211
  * @pattern: bytes to match where bitmask is 1
@@ -1708,7 +1770,7 @@ struct cfg80211_pmksa {
  * Internal note: @mask and @pattern are allocated in one chunk of
  * memory, free @mask only!
  */
-struct cfg80211_wowlan_trig_pkt_pattern {
+struct cfg80211_pkt_pattern {
        u8 *mask, *pattern;
        int pattern_len;
        int pkt_offset;
@@ -1770,12 +1832,41 @@ struct cfg80211_wowlan {
        bool any, disconnect, magic_pkt, gtk_rekey_failure,
             eap_identity_req, four_way_handshake,
             rfkill_release;
-       struct cfg80211_wowlan_trig_pkt_pattern *patterns;
+       struct cfg80211_pkt_pattern *patterns;
        struct cfg80211_wowlan_tcp *tcp;
        int n_patterns;
 };
 
 /**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ *     see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+       int delay;
+       enum nl80211_coalesce_condition condition;
+       struct cfg80211_pkt_pattern *patterns;
+       int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+       struct cfg80211_coalesce_rules *rules;
+       int n_rules;
+};
+
+/**
  * struct cfg80211_wowlan_wakeup - wakeup report
  * @disconnect: woke up by getting disconnected
  * @magic_pkt: woke up by receiving magic packet
@@ -1990,7 +2081,7 @@ struct cfg80211_update_ft_ies_params {
  * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management
  *     frame on another channel
  *
- * @testmode_cmd: run a test mode command
+ * @testmode_cmd: run a test mode command; @wdev may be %NULL
  * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be
  *     used by the function, but 0 and 1 must not be touched. Additionally,
  *     return error codes other than -ENOBUFS and -ENOENT will terminate the
@@ -2071,6 +2162,9 @@ struct cfg80211_update_ft_ies_params {
  *     driver can take the most appropriate actions.
  * @crit_proto_stop: Indicates critical protocol no longer needs increased link
  *     reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA)
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2196,7 +2290,8 @@ struct cfg80211_ops {
        void    (*rfkill_poll)(struct wiphy *wiphy);
 
 #ifdef CONFIG_NL80211_TESTMODE
-       int     (*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
+       int     (*testmode_cmd)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                               void *data, int len);
        int     (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb,
                                 struct netlink_callback *cb,
                                 void *data, int len);
@@ -2306,6 +2401,12 @@ struct cfg80211_ops {
                                    u16 duration);
        void    (*crit_proto_stop)(struct wiphy *wiphy,
                                   struct wireless_dev *wdev);
+       int     (*set_coalesce)(struct wiphy *wiphy,
+                               struct cfg80211_coalesce *coalesce);
+
+       int     (*channel_switch)(struct wiphy *wiphy,
+                                 struct net_device *dev,
+                                 struct cfg80211_csa_settings *params);
 };
 
 /*
@@ -2371,6 +2472,8 @@ struct cfg80211_ops {
  * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
  * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
  * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
+ * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
+ *     beaconing mode (AP, IBSS, Mesh, ...).
  */
 enum wiphy_flags {
        WIPHY_FLAG_CUSTOM_REGULATORY            = BIT(0),
@@ -2395,6 +2498,7 @@ enum wiphy_flags {
        WIPHY_FLAG_OFFCHAN_TX                   = BIT(20),
        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL        = BIT(21),
        WIPHY_FLAG_SUPPORTS_5_10_MHZ            = BIT(22),
+       WIPHY_FLAG_HAS_CHANNEL_SWITCH           = BIT(23),
 };
 
 /**
@@ -2532,6 +2636,25 @@ struct wiphy_wowlan_support {
 };
 
 /**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ *     (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+       int n_rules;
+       int max_delay;
+       int n_patterns;
+       int pattern_max_len;
+       int pattern_min_len;
+       int max_pkt_offset;
+};
+
+/**
  * struct wiphy - wireless hardware description
  * @reg_notifier: the driver's regulatory notification callback,
  *     note that if your driver uses wiphy_apply_custom_regulatory()
@@ -2641,6 +2764,7 @@ struct wiphy_wowlan_support {
  *     802.11-2012 8.4.2.29 for the defined fields.
  * @extended_capabilities_mask: mask of the valid values
  * @extended_capabilities_len: length of the extended capabilities
+ * @coalesce: packet coalescing support information
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2750,6 +2874,8 @@ struct wiphy {
        const struct iw_handler_def *wext;
 #endif
 
+       const struct wiphy_coalesce_support *coalesce;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3063,11 +3189,13 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 /**
  * ieee80211_mandatory_rates - get mandatory rates for a given band
  * @sband: the band to look for rates in
+ * @scan_width: width of the control channel
  *
  * This function returns a bitmap of the mandatory rates for the given
  * band, bits are set according to the rate position in the bitrates array.
  */
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width);
 
 /*
  * Radiotap parsing functions -- for controlled injection support
@@ -3379,10 +3507,11 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy);
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
 
 /**
- * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
+ * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @mgmt: the management frame (probe response or beacon)
  * @len: length of the management frame
  * @signal: the signal strength, type depends on the wiphy's signal_type
@@ -3395,16 +3524,29 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_channel *channel,
                          struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp);
+                         s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width_frame(wiphy, channel,
+                                              NL80211_BSS_CHAN_WIDTH_20,
+                                              mgmt, len, signal, gfp);
+}
 
 /**
  * cfg80211_inform_bss - inform cfg80211 of a new BSS
  *
  * @wiphy: the wiphy reporting the BSS
  * @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
  * @bssid: the BSSID of the BSS
  * @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
  * @capability: the capability field sent by the peer
@@ -3421,11 +3563,26 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
  * Or %NULL on error.
  */
 struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
 cfg80211_inform_bss(struct wiphy *wiphy,
                    struct ieee80211_channel *channel,
                    const u8 *bssid, u64 tsf, u16 capability,
                    u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp);
+                   s32 signal, gfp_t gfp)
+{
+       return cfg80211_inform_bss_width(wiphy, channel,
+                                        NL80211_BSS_CHAN_WIDTH_20,
+                                        bssid, tsf, capability,
+                                        beacon_interval, ie, ielen, signal,
+                                        gfp);
+}
 
 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      struct ieee80211_channel *channel,
@@ -3471,6 +3628,19 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
  */
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return NL80211_BSS_CHAN_WIDTH_5;
+       case NL80211_CHAN_WIDTH_10:
+               return NL80211_BSS_CHAN_WIDTH_10;
+       default:
+               return NL80211_BSS_CHAN_WIDTH_20;
+       }
+}
+
 /**
  * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
index c6d07cb..8b5b714 100644 (file)
@@ -230,6 +230,10 @@ enum ieee80211_radiotap_type {
 #define        IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
 #define        IEEE80211_CHAN_DYN      0x0400  /* Dynamic CCK-OFDM channel */
 #define        IEEE80211_CHAN_GFSK     0x0800  /* GFSK channel (FHSS PHY) */
+#define        IEEE80211_CHAN_GSM      0x1000  /* GSM (900 MHz) */
+#define        IEEE80211_CHAN_STURBO   0x2000  /* Static Turbo */
+#define        IEEE80211_CHAN_HALF     0x4000  /* Half channel (10 MHz wide) */
+#define        IEEE80211_CHAN_QUARTER  0x8000  /* Quarter channel (5 MHz wide) */
 
 /* For IEEE80211_RADIOTAP_FLAGS */
 #define        IEEE80211_RADIOTAP_F_CFP        0x01    /* sent/received
index 551ba6a..4be8785 100644 (file)
@@ -152,11 +152,14 @@ struct ieee80211_low_level_stats {
  * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
  * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
  * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
+ * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
+ *     this is used only with channel switching with CSA
  */
 enum ieee80211_chanctx_change {
        IEEE80211_CHANCTX_CHANGE_WIDTH          = BIT(0),
        IEEE80211_CHANCTX_CHANGE_RX_CHAINS      = BIT(1),
        IEEE80211_CHANCTX_CHANGE_RADAR          = BIT(2),
+       IEEE80211_CHANCTX_CHANGE_CHANNEL        = BIT(3),
 };
 
 /**
@@ -372,7 +375,7 @@ struct ieee80211_bss_conf {
 };
 
 /**
- * enum mac80211_tx_control_flags - flags to describe transmission information/status
+ * enum mac80211_tx_info_flags - flags to describe transmission information/status
  *
  * These flags are used with the @flags member of &ieee80211_tx_info.
  *
@@ -468,7 +471,7 @@ struct ieee80211_bss_conf {
  * Note: If you have to add new flags to the enumeration, then don't
  *      forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
  */
-enum mac80211_tx_control_flags {
+enum mac80211_tx_info_flags {
        IEEE80211_TX_CTL_REQ_TX_STATUS          = BIT(0),
        IEEE80211_TX_CTL_ASSIGN_SEQ             = BIT(1),
        IEEE80211_TX_CTL_NO_ACK                 = BIT(2),
@@ -504,6 +507,18 @@ enum mac80211_tx_control_flags {
 
 #define IEEE80211_TX_CTL_STBC_SHIFT            23
 
+/**
+ * enum mac80211_tx_control_flags - flags to describe transmit control
+ *
+ * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
+ *     protocol frame (e.g. EAP)
+ *
+ * These flags are used in tx_info->control.flags.
+ */
+enum mac80211_tx_control_flags {
+       IEEE80211_TX_CTRL_PORT_CTRL_PROTO       = BIT(0),
+};
+
 /*
  * This definition is used as a mask to clear all temporary flags, which are
  * set by the tx handlers for each transmission attempt by the mac80211 stack.
@@ -677,7 +692,8 @@ struct ieee80211_tx_info {
                        /* NB: vif can be NULL for injected frames */
                        struct ieee80211_vif *vif;
                        struct ieee80211_key_conf *hw_key;
-                       /* 8 bytes free */
+                       u32 flags;
+                       /* 4 bytes free */
                } control;
                struct {
                        struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
@@ -811,6 +827,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
  *     is stored in the @ampdu_delimiter_crc field)
  * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -839,6 +857,8 @@ enum mac80211_rx_flags {
        RX_FLAG_80P80MHZ                = BIT(24),
        RX_FLAG_160MHZ                  = BIT(25),
        RX_FLAG_STBC_MASK               = BIT(26) | BIT(27),
+       RX_FLAG_10MHZ                   = BIT(28),
+       RX_FLAG_5MHZ                    = BIT(29),
 };
 
 #define RX_FLAG_STBC_SHIFT             26
@@ -1004,11 +1024,11 @@ enum ieee80211_smps_mode {
  * @radar_enabled: whether radar detection is enabled
  *
  * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
- *    (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
- *    but actually means the number of transmissions not the number of retries
+ *     (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ *     but actually means the number of transmissions not the number of retries
  * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
- *    frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
- *    number of transmissions not the number of retries
+ *     frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ *     number of transmissions not the number of retries
  *
  * @smps_mode: spatial multiplexing powersave mode; note that
  *     %IEEE80211_SMPS_STATIC is used when the device is not
@@ -1080,6 +1100,7 @@ enum ieee80211_vif_flags {
  * @addr: address of this interface
  * @p2p: indicates whether this AP or STA interface is a p2p
  *     interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on
  * @driver_flags: flags/capabilities the driver has for this interface,
  *     these need to be set (or cleared) when the interface is added
  *     or, if supported by the driver, the interface type is changed
@@ -1092,7 +1113,7 @@ enum ieee80211_vif_flags {
  *     be off when it is %NULL there can still be races and packets could be
  *     processed after it switches back to %NULL.
  * @debugfs_dir: debugfs dentry, can be used by drivers to create own per
- *      interface debug files. Note that it will be NULL for the virtual
+ *     interface debug files. Note that it will be NULL for the virtual
  *     monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
@@ -1102,6 +1123,7 @@ struct ieee80211_vif {
        struct ieee80211_bss_conf bss_conf;
        u8 addr[ETH_ALEN];
        bool p2p;
+       bool csa_active;
 
        u8 cab_queue;
        u8 hw_queue[IEEE80211_NUM_ACS];
@@ -1425,10 +1447,10 @@ struct ieee80211_tx_control {
  *     the stack.
  *
  * @IEEE80211_HW_CONNECTION_MONITOR:
- *      The hardware performs its own connection monitoring, including
- *      periodic keep-alives to the AP and probing the AP on beacon loss.
- *      When this flag is set, signaling beacon-loss will cause an immediate
- *      change to disassociated state.
+ *     The hardware performs its own connection monitoring, including
+ *     periodic keep-alives to the AP and probing the AP on beacon loss.
+ *     When this flag is set, signaling beacon-loss will cause an immediate
+ *     change to disassociated state.
  *
  * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
  *     This device needs to get data from beacon before association (i.e.
@@ -1527,10 +1549,10 @@ enum ieee80211_hw_flags {
  * @channel_change_time: time (in microseconds) it takes to change channels.
  *
  * @max_signal: Maximum value for signal (rssi) in RX information, used
- *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
+ *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
  *
  * @max_listen_interval: max listen interval in units of beacon interval
- *     that HW supports
+ *     that HW supports
  *
  * @queues: number of available hardware transmit queues for
  *     data packets. WMM/QoS requires at least four, these
@@ -2444,7 +2466,7 @@ enum ieee80211_roc_type {
  *     The callback can sleep.
  *
  * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
- *      Currently, this is only used for IBSS mode debugging. Is not a
+ *     Currently, this is only used for IBSS mode debugging. Is not a
  *     required function.
  *     The callback can sleep.
  *
@@ -2495,8 +2517,8 @@ enum ieee80211_roc_type {
  *     in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout
  *     accordingly. This callback is not required and may sleep.
  *
- * @testmode_cmd: Implement a cfg80211 test mode command.
- *     The callback can sleep.
+ * @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may
+ *     be %NULL. The callback can sleep.
  * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
  *
  * @flush: Flush all pending frames from the hardware queue, making sure
@@ -2634,6 +2656,16 @@ enum ieee80211_roc_type {
  * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
  *     Currently, this is only called for managed or P2P client interfaces.
  *     This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch to a new channel.
+ *     Beacons are modified to include CSA or ECSA IEs before calling this
+ *     function. The corresponding count fields in these IEs must be
+ *     decremented, and when they reach zero the driver must call
+ *     ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
+ *     get the csa counter decremented by mac80211, but must check if it is
+ *     zero using ieee80211_csa_is_complete() after the beacon has been
+ *     transmitted and then call ieee80211_csa_finish().
+ *
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2747,7 +2779,8 @@ struct ieee80211_ops {
        void (*rfkill_poll)(struct ieee80211_hw *hw);
        void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
 #ifdef CONFIG_NL80211_TESTMODE
-       int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
+       int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                           void *data, int len);
        int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb,
                             struct netlink_callback *cb,
                             void *data, int len);
@@ -2821,6 +2854,9 @@ struct ieee80211_ops {
                                 struct ieee80211_vif *vif,
                                 struct inet6_dev *idev);
 #endif
+       void (*channel_switch_beacon)(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct cfg80211_chan_def *chandef);
 };
 
 /**
@@ -3316,6 +3352,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 }
 
 /**
+ * ieee80211_csa_finish - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hit zero, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_csa_finish(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_is_complete - find out if counters reached zero
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * This function returns whether the channel switch counters reached zero.
+ */
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
+
+
+/**
  * ieee80211_proberesp_get - retrieve a Probe Response template
  * @hw: pointer obtained from ieee80211_alloc_hw().
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
@@ -3634,6 +3689,89 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
                              int tid, struct ieee80211_key_seq *seq);
 
 /**
+ * ieee80211_set_key_tx_seq - set key TX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current TX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and the
+ * device may have transmitted frames using the PTK, e.g. replies to
+ * ARP requests.
+ *
+ * Note that this function may only be called when no TX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+                             struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_set_key_rx_seq - set key RX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @tid: The TID, or -1 for the management frame value (CCMP only);
+ *     the value on TID 0 is also used for non-QoS frames. For
+ *     CMAC, only TID 0 is valid.
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current RX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and GTK
+ * rekey may have been done while suspended. It should not be called
+ * if IV checking is done by the device and not by mac80211.
+ *
+ * Note that this function may only be called when no RX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+                             int tid, struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_remove_key - remove the given key
+ * @keyconf: the parameter passed with the set key
+ *
+ * Remove the given key. If the key was uploaded to the hardware at the
+ * time this function is called, it is not deleted in the hardware but
+ * instead assumed to have been removed already.
+ *
+ * Note that due to locking considerations this function can (currently)
+ * only be called during key iteration (ieee80211_iter_keys().)
+ */
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf);
+
+/**
+ * ieee80211_gtk_rekey_add - add a GTK key from rekeying during WoWLAN
+ * @vif: the virtual interface to add the key on
+ * @keyconf: new key data
+ *
+ * When GTK rekeying was done while the system was suspended, (a) new
+ * key(s) will be available. These will be needed by mac80211 for proper
+ * RX processing, so this function allows setting them.
+ *
+ * The function returns the newly allocated key structure, which will
+ * have similar contents to the passed key configuration but point to
+ * mac80211-owned memory. In case of errors, the function returns an
+ * ERR_PTR(), use IS_ERR() etc.
+ *
+ * Note that this function assumes the key isn't added to hardware
+ * acceleration, so no TX will be done with the key. Since it's a GTK
+ * on managed (station) networks, this is true anyway. If the driver
+ * calls this function from the resume callback and subsequently uses
+ * the return code 1 to reconfigure the device, this key will be part
+ * of the reconfiguration.
+ *
+ * Note that the driver should also call ieee80211_set_key_rx_seq()
+ * for the new key for each TID to set up sequence counters properly.
+ *
+ * IMPORTANT: If this replaces a key that is present in the hardware,
+ * then it will attempt to remove it during this call. In many cases
+ * this isn't what you want, so call ieee80211_remove_key() first for
+ * the key that's being replaced.
+ */
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+                       struct ieee80211_key_conf *keyconf);
+
+/**
  * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
  * @vif: virtual interface the rekeying was done on
  * @bssid: The BSSID of the AP, for checking association
@@ -4205,8 +4343,10 @@ struct rate_control_ops {
 
        void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
        void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+                         struct cfg80211_chan_def *chandef,
                          struct ieee80211_sta *sta, void *priv_sta);
        void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+                           struct cfg80211_chan_def *chandef,
                            struct ieee80211_sta *sta, void *priv_sta,
                            u32 changed);
        void (*free_sta)(void *priv, struct ieee80211_sta *sta,
index 5f286b7..f68ee68 100644 (file)
@@ -224,6 +224,9 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev,
                                 u8 *gt, u8 gt_len);
 u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
 
+int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+                        u32 result);
+
 int nfc_targets_found(struct nfc_dev *dev,
                      struct nfc_target *targets, int ntargets);
 int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
index 8137dd8..29bed72 100644 (file)
  * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
  * @NFC_CMD_FW_DOWNLOAD: Request to Load/flash firmware, or event to inform
  *     that some firmware was loaded
+ * @NFC_EVENT_SE_ADDED: Event emitted when a new secure element is discovered.
+ *     This typically will be sent whenever a new NFC controller with either
+ *     an embedded SE or an UICC one connected to it through SWP.
+ * @NFC_EVENT_SE_REMOVED: Event emitted when a secure element is removed from
+ *     the system, as a consequence of e.g. an NFC controller being unplugged.
+ * @NFC_EVENT_SE_CONNECTIVITY: This event is emitted whenever a secure element
+ *     is requesting connectivity access. For example a UICC SE may need to
+ *     talk with a sleeping modem and will notify this need by sending this
+ *     event. It is then up to userspace to decide if it will wake the modem
+ *     up or not.
+ * @NFC_EVENT_SE_TRANSACTION: This event is sent when an application running on
+ *     a specific SE notifies us about the end of a transaction. The parameter
+ *     for this event is the application ID (AID).
+ * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -97,6 +111,9 @@ enum nfc_commands {
        NFC_CMD_FW_DOWNLOAD,
        NFC_EVENT_SE_ADDED,
        NFC_EVENT_SE_REMOVED,
+       NFC_EVENT_SE_CONNECTIVITY,
+       NFC_EVENT_SE_TRANSACTION,
+       NFC_CMD_GET_SE,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -129,6 +146,7 @@ enum nfc_commands {
  * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
  * @NFC_ATTR_SE_INDEX: Secure element index
  * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
+ * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -154,6 +172,8 @@ enum nfc_attrs {
        NFC_ATTR_FIRMWARE_NAME,
        NFC_ATTR_SE_INDEX,
        NFC_ATTR_SE_TYPE,
+       NFC_ATTR_SE_AID,
+       NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
index 861e5eb..1f42bc3 100644 (file)
  */
 
 /**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *     return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *     the new channel information (Channel Switch Announcement - CSA)
+ *     in the beacon for some time (as defined in the
+ *     %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *     new channel. Userspace provides the new channel information (using
+ *     %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *     width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *     other station that transmission must be blocked until the channel
+ *     switch is complete.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +848,11 @@ enum nl80211_commands {
        NL80211_CMD_CRIT_PROTOCOL_START,
        NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+       NL80211_CMD_GET_COALESCE,
+       NL80211_CMD_SET_COALESCE,
+
+       NL80211_CMD_CHANNEL_SWITCH,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1436,6 +1479,20 @@ enum nl80211_commands {
  *     allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *     update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *     until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *     must be blocked on the current channel (before the channel switch
+ *     operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *     for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ *     field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ *     field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,6 +1793,14 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PEER_AID,
 
+       NL80211_ATTR_COALESCE_RULE,
+
+       NL80211_ATTR_CH_SWITCH_COUNT,
+       NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+       NL80211_ATTR_CSA_IES,
+       NL80211_ATTR_CSA_C_OFF_BEACON,
+       NL80211_ATTR_CSA_C_OFF_PRESP,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2773,6 +2838,21 @@ enum nl80211_chan_width {
 };
 
 /**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+       NL80211_BSS_CHAN_WIDTH_20,
+       NL80211_BSS_CHAN_WIDTH_10,
+       NL80211_BSS_CHAN_WIDTH_5,
+};
+
+/**
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
@@ -2796,6 +2876,8 @@ enum nl80211_chan_width {
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *     elements from a Beacon frame (bin); not present if no Beacon frame has
  *     yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *     (u32, enum nl80211_bss_scan_width)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2812,6 +2894,7 @@ enum nl80211_bss {
        NL80211_BSS_STATUS,
        NL80211_BSS_SEEN_MS_AGO,
        NL80211_BSS_BEACON_IES,
+       NL80211_BSS_CHAN_WIDTH,
 
        /* keep last */
        __NL80211_BSS_AFTER_LAST,
@@ -3060,11 +3143,11 @@ enum nl80211_tx_power_setting {
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *     a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *     a bit for each byte in the pattern. The lowest-order bit corresponds
  *     to the first byte of the pattern, but the bytes of the pattern are
  *     in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3075,39 +3158,50 @@ enum nl80211_tx_power_setting {
  *     Note that the pattern matching is done as though frames were not
  *     802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *     first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *     these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-       __NL80211_WOWLAN_PKTPAT_INVALID,
-       NL80211_WOWLAN_PKTPAT_MASK,
-       NL80211_WOWLAN_PKTPAT_PATTERN,
-       NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+       __NL80211_PKTPAT_INVALID,
+       NL80211_PKTPAT_MASK,
+       NL80211_PKTPAT_PATTERN,
+       NL80211_PKTPAT_OFFSET,
 
-       NUM_NL80211_WOWLAN_PKTPAT,
-       MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+       NUM_NL80211_PKTPAT,
+       MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
        __u32 max_patterns;
        __u32 min_pattern_len;
        __u32 max_pattern_len;
        __u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3127,7 +3221,7 @@ struct nl80211_wowlan_pattern_support {
  *     pattern matching is done after the packet is converted to the MSDU.
  *
  *     In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *     carrying a &struct nl80211_wowlan_pattern_support.
+ *     carrying a &struct nl80211_pattern_support.
  *
  *     When reporting wakeup. it is a u32 attribute containing the 0-based
  *     index of the pattern that caused the wakeup, in the patterns passed
@@ -3284,7 +3378,7 @@ struct nl80211_wowlan_tcp_data_token_feature {
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *     u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *     feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *     feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *     but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3309,6 +3403,55 @@ enum nl80211_wowlan_tcp_attrs {
 };
 
 /**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+       __u32 max_rules;
+       struct nl80211_pattern_support pat;
+       __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *     see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *     after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+       __NL80211_COALESCE_RULE_INVALID,
+       NL80211_ATTR_COALESCE_RULE_DELAY,
+       NL80211_ATTR_COALESCE_RULE_CONDITION,
+       NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+       /* keep last */
+       NUM_NL80211_ATTR_COALESCE_RULE,
+       NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *     in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *     in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+       NL80211_COALESCE_CONDITION_MATCH,
+       NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
  * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
index 6c7f363..f081712 100644 (file)
 #include <net/bluetooth/a2mp.h>
 #include <net/bluetooth/smp.h>
 
+struct sco_param {
+       u16 pkt_type;
+       u16 max_latency;
+};
+
+static const struct sco_param sco_param_cvsd[] = {
+       { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a }, /* S3 */
+       { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007 }, /* S2 */
+       { EDR_ESCO_MASK | ESCO_EV3,   0x0007 }, /* S1 */
+       { EDR_ESCO_MASK | ESCO_HV3,   0xffff }, /* D1 */
+       { EDR_ESCO_MASK | ESCO_HV1,   0xffff }, /* D0 */
+};
+
+static const struct sco_param sco_param_wideband[] = {
+       { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d }, /* T2 */
+       { EDR_ESCO_MASK | ESCO_EV3,   0x0008 }, /* T1 */
+};
+
 static void hci_le_create_connection(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
@@ -172,10 +190,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle)
        hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
 }
 
-void hci_setup_sync(struct hci_conn *conn, __u16 handle)
+bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
 {
        struct hci_dev *hdev = conn->hdev;
        struct hci_cp_setup_sync_conn cp;
+       const struct sco_param *param;
 
        BT_DBG("hcon %p", conn);
 
@@ -185,15 +204,35 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
        conn->attempt++;
 
        cp.handle   = cpu_to_le16(handle);
-       cp.pkt_type = cpu_to_le16(conn->pkt_type);
 
        cp.tx_bandwidth   = __constant_cpu_to_le32(0x00001f40);
        cp.rx_bandwidth   = __constant_cpu_to_le32(0x00001f40);
-       cp.max_latency    = __constant_cpu_to_le16(0xffff);
-       cp.voice_setting  = cpu_to_le16(hdev->voice_setting);
-       cp.retrans_effort = 0xff;
+       cp.voice_setting  = cpu_to_le16(conn->setting);
+
+       switch (conn->setting & SCO_AIRMODE_MASK) {
+       case SCO_AIRMODE_TRANSP:
+               if (conn->attempt > ARRAY_SIZE(sco_param_wideband))
+                       return false;
+               cp.retrans_effort = 0x02;
+               param = &sco_param_wideband[conn->attempt - 1];
+               break;
+       case SCO_AIRMODE_CVSD:
+               if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
+                       return false;
+               cp.retrans_effort = 0x01;
+               param = &sco_param_cvsd[conn->attempt - 1];
+               break;
+       default:
+               return false;
+       }
 
-       hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
+       cp.pkt_type = __cpu_to_le16(param->pkt_type);
+       cp.max_latency = __cpu_to_le16(param->max_latency);
+
+       if (hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
+               return false;
+
+       return true;
 }
 
 void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
@@ -560,13 +599,13 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
        return acl;
 }
 
-static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
-                               bdaddr_t *dst, u8 sec_level, u8 auth_type)
+struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
+                                __u16 setting)
 {
        struct hci_conn *acl;
        struct hci_conn *sco;
 
-       acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
+       acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
        if (IS_ERR(acl))
                return acl;
 
@@ -584,6 +623,8 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
 
        hci_conn_hold(sco);
 
+       sco->setting = setting;
+
        if (acl->state == BT_CONNECTED &&
            (sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
                set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
@@ -612,9 +653,6 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
                return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
        case ACL_LINK:
                return hci_connect_acl(hdev, dst, sec_level, auth_type);
-       case SCO_LINK:
-       case ESCO_LINK:
-               return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
        }
 
        return ERR_PTR(-EINVAL);
index cc27297..634deba 100644 (file)
@@ -454,6 +454,18 @@ static void hci_setup_event_mask(struct hci_request *req)
                events[4] |= 0x04; /* Read Remote Extended Features Complete */
                events[5] |= 0x08; /* Synchronous Connection Complete */
                events[5] |= 0x10; /* Synchronous Connection Changed */
+       } else {
+               /* Use a different default for LE-only devices */
+               memset(events, 0, sizeof(events));
+               events[0] |= 0x10; /* Disconnection Complete */
+               events[0] |= 0x80; /* Encryption Change */
+               events[1] |= 0x08; /* Read Remote Version Information Complete */
+               events[1] |= 0x20; /* Command Complete */
+               events[1] |= 0x40; /* Command Status */
+               events[1] |= 0x80; /* Hardware Error */
+               events[2] |= 0x04; /* Number of Completed Packets */
+               events[3] |= 0x02; /* Data Buffer Overflow */
+               events[5] |= 0x80; /* Encryption Key Refresh Complete */
        }
 
        if (lmp_inq_rssi_capable(hdev))
@@ -608,7 +620,7 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
         * as supported send it. If not supported assume that the controller
         * does not have actual support for stored link keys which makes this
         * command redundant anyway.
-         */
+        */
        if (hdev->commands[6] & 0x80) {
                struct hci_cp_delete_stored_link_key cp;
 
index 0437200..94aab73 100644 (file)
@@ -2904,15 +2904,16 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
                hci_conn_add_sysfs(conn);
                break;
 
+       case 0x0d:      /* Connection Rejected due to Limited Resources */
        case 0x11:      /* Unsupported Feature or Parameter Value */
        case 0x1c:      /* SCO interval rejected */
        case 0x1a:      /* Unsupported Remote Feature */
        case 0x1f:      /* Unspecified error */
-               if (conn->out && conn->attempt < 2) {
+               if (conn->out) {
                        conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
                                        (hdev->esco_type & EDR_ESCO_MASK);
-                       hci_setup_sync(conn, conn->link->handle);
-                       goto unlock;
+                       if (hci_setup_sync(conn, conn->link->handle))
+                               goto unlock;
                }
                /* fall through */
 
@@ -3024,17 +3025,20 @@ unlock:
 static u8 hci_get_auth_req(struct hci_conn *conn)
 {
        /* If remote requests dedicated bonding follow that lead */
-       if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
+       if (conn->remote_auth == HCI_AT_DEDICATED_BONDING ||
+           conn->remote_auth == HCI_AT_DEDICATED_BONDING_MITM) {
                /* If both remote and local IO capabilities allow MITM
                 * protection then require it, otherwise don't */
-               if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
-                       return 0x02;
+               if (conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT ||
+                   conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+                       return HCI_AT_DEDICATED_BONDING;
                else
-                       return 0x03;
+                       return HCI_AT_DEDICATED_BONDING_MITM;
        }
 
        /* If remote requests no-bonding follow that lead */
-       if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
+       if (conn->remote_auth == HCI_AT_NO_BONDING ||
+           conn->remote_auth == HCI_AT_NO_BONDING_MITM)
                return conn->remote_auth | (conn->auth_type & 0x01);
 
        return conn->auth_type;
@@ -3066,7 +3070,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                /* Change the IO capability from KeyboardDisplay
                 * to DisplayYesNo as it is not supported by BT spec. */
                cp.capability = (conn->io_capability == 0x04) ?
-                                               0x01 : conn->io_capability;
+                               HCI_IO_DISPLAY_YESNO : conn->io_capability;
                conn->auth_type = hci_get_auth_req(conn);
                cp.authentication = conn->auth_type;
 
@@ -3140,7 +3144,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
         * request. The only exception is when we're dedicated bonding
         * initiators (connect_cfm_cb set) since then we always have the MITM
         * bit set. */
-       if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) {
+       if (!conn->connect_cfm_cb && loc_mitm &&
+           conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
                BT_DBG("Rejecting request: remote device can't provide MITM");
                hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
                             sizeof(ev->bdaddr), &ev->bdaddr);
@@ -3148,8 +3153,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
        }
 
        /* If no side requires MITM protection; auto-accept */
-       if ((!loc_mitm || conn->remote_cap == 0x03) &&
-           (!rem_mitm || conn->io_capability == 0x03)) {
+       if ((!loc_mitm || conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) &&
+           (!rem_mitm || conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)) {
 
                /* If we're not the initiators request authorization to
                 * proceed from user space (mgmt_user_confirm with
index 0c699cd..13863de 100644 (file)
@@ -238,6 +238,31 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep
        return hidp_send_intr_message(session, hdr, buf, rsize);
 }
 
+static int hidp_hidinput_event(struct input_dev *dev, unsigned int type,
+                              unsigned int code, int value)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct hidp_session *session = hid->driver_data;
+       struct hid_field *field;
+       int offset;
+
+       BT_DBG("session %p type %d code %d value %d",
+              session, type, code, value);
+
+       if (type != EV_LED)
+               return -1;
+
+       offset = hidinput_find_field(hid, type, code, &field);
+       if (offset == -1) {
+               hid_warn(dev, "event field not found\n");
+               return -1;
+       }
+
+       hid_set_field(field, offset, value);
+
+       return hidp_send_report(session, field->report);
+}
+
 static int hidp_get_raw_report(struct hid_device *hid,
                unsigned char report_number,
                unsigned char *data, size_t count,
@@ -678,20 +703,6 @@ static int hidp_parse(struct hid_device *hid)
 
 static int hidp_start(struct hid_device *hid)
 {
-       struct hidp_session *session = hid->driver_data;
-       struct hid_report *report;
-
-       if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
-               return 0;
-
-       list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
-                       report_list, list)
-               hidp_send_report(session, report);
-
-       list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
-                       report_list, list)
-               hidp_send_report(session, report);
-
        return 0;
 }
 
@@ -711,6 +722,7 @@ static struct hid_ll_driver hidp_hid_driver = {
        .stop = hidp_stop,
        .open  = hidp_open,
        .close = hidp_close,
+       .hidinput_input_event = hidp_hidinput_event,
 };
 
 /* This function sets up the hid device. It does not add it
index 8c3499b..b3bb7bc 100644 (file)
@@ -1415,8 +1415,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                        sk->sk_state_change(sk);
                        release_sock(sk);
 
-               } else if (chan->state == BT_CONNECT)
+               } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
+               }
 
                l2cap_chan_unlock(chan);
        }
index b6e44ad..6d126fa 100644 (file)
@@ -58,7 +58,6 @@ struct rfcomm_dev {
        uint                    modem_status;
 
        struct rfcomm_dlc       *dlc;
-       wait_queue_head_t       wait;
 
        struct device           *tty_dev;
 
@@ -76,13 +75,6 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
 
 /* ---- Device functions ---- */
 
-/*
- * The reason this isn't actually a race, as you no doubt have a little voice
- * screaming at you in your head, is that the refcount should never actually
- * reach zero unless the device has already been taken off the list, in
- * rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in
- * rfcomm_dev_destruct() anyway.
- */
 static void rfcomm_dev_destruct(struct tty_port *port)
 {
        struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
@@ -90,10 +82,9 @@ static void rfcomm_dev_destruct(struct tty_port *port)
 
        BT_DBG("dev %p dlc %p", dev, dlc);
 
-       /* Refcount should only hit zero when called from rfcomm_dev_del()
-          which will have taken us off the list. Everything else are
-          refcounting bugs. */
-       BUG_ON(!list_empty(&dev->list));
+       spin_lock(&rfcomm_dev_lock);
+       list_del(&dev->list);
+       spin_unlock(&rfcomm_dev_lock);
 
        rfcomm_dlc_lock(dlc);
        /* Detach DLC if it's owned by this dev */
@@ -112,8 +103,39 @@ static void rfcomm_dev_destruct(struct tty_port *port)
        module_put(THIS_MODULE);
 }
 
+/* device-specific initialization: open the dlc */
+static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
+{
+       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+       return rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
+}
+
+/* we block the open until the dlc->state becomes BT_CONNECTED */
+static int rfcomm_dev_carrier_raised(struct tty_port *port)
+{
+       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+       return (dev->dlc->state == BT_CONNECTED);
+}
+
+/* device-specific cleanup: close the dlc */
+static void rfcomm_dev_shutdown(struct tty_port *port)
+{
+       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+       if (dev->tty_dev->parent)
+               device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
+
+       /* close the dlc */
+       rfcomm_dlc_close(dev->dlc, 0);
+}
+
 static const struct tty_port_operations rfcomm_port_ops = {
        .destruct = rfcomm_dev_destruct,
+       .activate = rfcomm_dev_activate,
+       .shutdown = rfcomm_dev_shutdown,
+       .carrier_raised = rfcomm_dev_carrier_raised,
 };
 
 static struct rfcomm_dev *__rfcomm_dev_get(int id)
@@ -236,7 +258,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 
        tty_port_init(&dev->port);
        dev->port.ops = &rfcomm_port_ops;
-       init_waitqueue_head(&dev->wait);
 
        skb_queue_head_init(&dev->pending);
 
@@ -282,7 +303,9 @@ out:
                        dev->id, NULL);
        if (IS_ERR(dev->tty_dev)) {
                err = PTR_ERR(dev->tty_dev);
+               spin_lock(&rfcomm_dev_lock);
                list_del(&dev->list);
+               spin_unlock(&rfcomm_dev_lock);
                goto free;
        }
 
@@ -301,27 +324,6 @@ free:
        return err;
 }
 
-static void rfcomm_dev_del(struct rfcomm_dev *dev)
-{
-       unsigned long flags;
-       BT_DBG("dev %p", dev);
-
-       BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
-
-       spin_lock_irqsave(&dev->port.lock, flags);
-       if (dev->port.count > 0) {
-               spin_unlock_irqrestore(&dev->port.lock, flags);
-               return;
-       }
-       spin_unlock_irqrestore(&dev->port.lock, flags);
-
-       spin_lock(&rfcomm_dev_lock);
-       list_del_init(&dev->list);
-       spin_unlock(&rfcomm_dev_lock);
-
-       tty_port_put(&dev->port);
-}
-
 /* ---- Send buffer ---- */
 static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
 {
@@ -333,10 +335,9 @@ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
 static void rfcomm_wfree(struct sk_buff *skb)
 {
        struct rfcomm_dev *dev = (void *) skb->sk;
-       struct tty_struct *tty = dev->port.tty;
        atomic_sub(skb->truesize, &dev->wmem_alloc);
-       if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty)
-               tty_wakeup(tty);
+       if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
+               tty_port_tty_wakeup(&dev->port);
        tty_port_put(&dev->port);
 }
 
@@ -410,6 +411,7 @@ static int rfcomm_release_dev(void __user *arg)
 {
        struct rfcomm_dev_req req;
        struct rfcomm_dev *dev;
+       struct tty_struct *tty;
 
        if (copy_from_user(&req, arg, sizeof(req)))
                return -EFAULT;
@@ -429,11 +431,15 @@ static int rfcomm_release_dev(void __user *arg)
                rfcomm_dlc_close(dev->dlc, 0);
 
        /* Shut down TTY synchronously before freeing rfcomm_dev */
-       if (dev->port.tty)
-               tty_vhangup(dev->port.tty);
+       tty = tty_port_tty_get(&dev->port);
+       if (tty) {
+               tty_vhangup(tty);
+               tty_kref_put(tty);
+       }
+
+       if (!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+               tty_port_put(&dev->port);
 
-       if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
-               rfcomm_dev_del(dev);
        tty_port_put(&dev->port);
        return 0;
 }
@@ -563,16 +569,21 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
 {
        struct rfcomm_dev *dev = dlc->owner;
+       struct tty_struct *tty;
        if (!dev)
                return;
 
        BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
 
        dev->err = err;
-       wake_up_interruptible(&dev->wait);
+       if (dlc->state == BT_CONNECTED) {
+               device_move(dev->tty_dev, rfcomm_get_device(dev),
+                           DPM_ORDER_DEV_AFTER_PARENT);
 
-       if (dlc->state == BT_CLOSED) {
-               if (!dev->port.tty) {
+               wake_up_interruptible(&dev->port.open_wait);
+       } else if (dlc->state == BT_CLOSED) {
+               tty = tty_port_tty_get(&dev->port);
+               if (!tty) {
                        if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
                                /* Drop DLC lock here to avoid deadlock
                                 * 1. rfcomm_dev_get will take rfcomm_dev_lock
@@ -580,6 +591,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
                                 *    rfcomm_dev_lock -> dlc lock
                                 * 2. tty_port_put will deadlock if it's
                                 *    the last reference
+                                *
+                                * FIXME: when we release the lock anything
+                                * could happen to dev, even its destruction
                                 */
                                rfcomm_dlc_unlock(dlc);
                                if (rfcomm_dev_get(dev->id) == NULL) {
@@ -587,12 +601,17 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
                                        return;
                                }
 
-                               rfcomm_dev_del(dev);
+                               if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
+                                                     &dev->flags))
+                                       tty_port_put(&dev->port);
+
                                tty_port_put(&dev->port);
                                rfcomm_dlc_lock(dlc);
                        }
-               } else
-                       tty_hangup(dev->port.tty);
+               } else {
+                       tty_hangup(tty);
+                       tty_kref_put(tty);
+               }
        }
 }
 
@@ -604,10 +623,8 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
 
        BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
 
-       if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) {
-               if (dev->port.tty && !C_CLOCAL(dev->port.tty))
-                       tty_hangup(dev->port.tty);
-       }
+       if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV))
+               tty_port_tty_hangup(&dev->port, true);
 
        dev->modem_status =
                ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
@@ -638,124 +655,92 @@ static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
                tty_flip_buffer_push(&dev->port);
 }
 
-static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+/* do the reverse of install, clearing the tty fields and releasing the
+ * reference to tty_port
+ */
+static void rfcomm_tty_cleanup(struct tty_struct *tty)
 {
-       DECLARE_WAITQUEUE(wait, current);
-       struct rfcomm_dev *dev;
-       struct rfcomm_dlc *dlc;
-       unsigned long flags;
-       int err, id;
+       struct rfcomm_dev *dev = tty->driver_data;
 
-       id = tty->index;
+       clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
 
-       BT_DBG("tty %p id %d", tty, id);
+       rfcomm_dlc_lock(dev->dlc);
+       tty->driver_data = NULL;
+       rfcomm_dlc_unlock(dev->dlc);
 
-       /* We don't leak this refcount. For reasons which are not entirely
-          clear, the TTY layer will call our ->close() method even if the
-          open fails. We decrease the refcount there, and decreasing it
-          here too would cause breakage. */
-       dev = rfcomm_dev_get(id);
-       if (!dev)
-               return -ENODEV;
+       /*
+        * purge the dlc->tx_queue to avoid circular dependencies
+        * between dev and dlc
+        */
+       skb_queue_purge(&dev->dlc->tx_queue);
 
-       BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
-              dev->channel, dev->port.count);
+       tty_port_put(&dev->port);
+}
 
-       spin_lock_irqsave(&dev->port.lock, flags);
-       if (++dev->port.count > 1) {
-               spin_unlock_irqrestore(&dev->port.lock, flags);
-               return 0;
-       }
-       spin_unlock_irqrestore(&dev->port.lock, flags);
+/* we acquire the tty_port reference since it's here the tty is first used
+ * by setting the termios. We also populate the driver_data field and install
+ * the tty port
+ */
+static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+       struct rfcomm_dev *dev;
+       struct rfcomm_dlc *dlc;
+       int err;
+
+       dev = rfcomm_dev_get(tty->index);
+       if (!dev)
+               return -ENODEV;
 
        dlc = dev->dlc;
 
        /* Attach TTY and open DLC */
-
        rfcomm_dlc_lock(dlc);
        tty->driver_data = dev;
-       dev->port.tty = tty;
        rfcomm_dlc_unlock(dlc);
        set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
 
-       err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
-       if (err < 0)
-               return err;
-
-       /* Wait for DLC to connect */
-       add_wait_queue(&dev->wait, &wait);
-       while (1) {
-               set_current_state(TASK_INTERRUPTIBLE);
+       /* install the tty_port */
+       err = tty_port_install(&dev->port, driver, tty);
+       if (err)
+               rfcomm_tty_cleanup(tty);
 
-               if (dlc->state == BT_CLOSED) {
-                       err = -dev->err;
-                       break;
-               }
+       return err;
+}
 
-               if (dlc->state == BT_CONNECTED)
-                       break;
+static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+       struct rfcomm_dev *dev = tty->driver_data;
+       int err;
 
-               if (signal_pending(current)) {
-                       err = -EINTR;
-                       break;
-               }
+       BT_DBG("tty %p id %d", tty, tty->index);
 
-               tty_unlock(tty);
-               schedule();
-               tty_lock(tty);
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&dev->wait, &wait);
+       BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
+              dev->channel, dev->port.count);
 
-       if (err == 0)
-               device_move(dev->tty_dev, rfcomm_get_device(dev),
-                           DPM_ORDER_DEV_AFTER_PARENT);
+       err = tty_port_open(&dev->port, tty, filp);
+       if (err)
+               return err;
 
+       /*
+        * FIXME: rfcomm should use proper flow control for
+        * received data. This hack will be unnecessary and can
+        * be removed when that's implemented
+        */
        rfcomm_tty_copy_pending(dev);
 
        rfcomm_dlc_unthrottle(dev->dlc);
 
-       return err;
+       return 0;
 }
 
 static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       unsigned long flags;
-
-       if (!dev)
-               return;
 
        BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
                                                dev->port.count);
 
-       spin_lock_irqsave(&dev->port.lock, flags);
-       if (!--dev->port.count) {
-               spin_unlock_irqrestore(&dev->port.lock, flags);
-               if (dev->tty_dev->parent)
-                       device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
-
-               /* Close DLC and dettach TTY */
-               rfcomm_dlc_close(dev->dlc, 0);
-
-               clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
-
-               rfcomm_dlc_lock(dev->dlc);
-               tty->driver_data = NULL;
-               dev->port.tty = NULL;
-               rfcomm_dlc_unlock(dev->dlc);
-
-               if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
-                       spin_lock(&rfcomm_dev_lock);
-                       list_del_init(&dev->list);
-                       spin_unlock(&rfcomm_dev_lock);
-
-                       tty_port_put(&dev->port);
-               }
-       } else
-               spin_unlock_irqrestore(&dev->port.lock, flags);
-
-       tty_port_put(&dev->port);
+       tty_port_close(&dev->port, tty, filp);
 }
 
 static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -1055,17 +1040,11 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
 
        BT_DBG("tty %p dev %p", tty, dev);
 
-       if (!dev)
-               return;
-
-       rfcomm_tty_flush_buffer(tty);
+       tty_port_hangup(&dev->port);
 
-       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
-               if (rfcomm_dev_get(dev->id) == NULL)
-                       return;
-               rfcomm_dev_del(dev);
+       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
+           !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
                tty_port_put(&dev->port);
-       }
 }
 
 static int rfcomm_tty_tiocmget(struct tty_struct *tty)
@@ -1128,6 +1107,8 @@ static const struct tty_operations rfcomm_ops = {
        .wait_until_sent        = rfcomm_tty_wait_until_sent,
        .tiocmget               = rfcomm_tty_tiocmget,
        .tiocmset               = rfcomm_tty_tiocmset,
+       .install                = rfcomm_tty_install,
+       .cleanup                = rfcomm_tty_cleanup,
 };
 
 int __init rfcomm_init_ttys(void)
@@ -1146,7 +1127,7 @@ int __init rfcomm_init_ttys(void)
        rfcomm_tty_driver->subtype      = SERIAL_TYPE_NORMAL;
        rfcomm_tty_driver->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        rfcomm_tty_driver->init_termios = tty_std_termios;
-       rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
        rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
 
index e7bd4ee..96bd388 100644 (file)
@@ -176,8 +176,13 @@ static int sco_connect(struct sock *sk)
        else
                type = SCO_LINK;
 
-       hcon = hci_connect(hdev, type, dst, BDADDR_BREDR, BT_SECURITY_LOW,
-                          HCI_AT_NO_BONDING);
+       if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
+           (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
+       hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
        if (IS_ERR(hcon)) {
                err = PTR_ERR(hcon);
                goto done;
@@ -417,6 +422,8 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int pro
        sk->sk_protocol = proto;
        sk->sk_state    = BT_OPEN;
 
+       sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
+
        setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
 
        bt_sock_link(&sco_sk_list, sk);
@@ -652,7 +659,7 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        return err;
 }
 
-static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
+static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting)
 {
        struct hci_dev *hdev = conn->hdev;
 
@@ -664,11 +671,7 @@ static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
                struct hci_cp_accept_conn_req cp;
 
                bacpy(&cp.bdaddr, &conn->dst);
-
-               if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
-                       cp.role = 0x00; /* Become master */
-               else
-                       cp.role = 0x01; /* Remain slave */
+               cp.role = 0x00; /* Ignored */
 
                hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
        } else {
@@ -679,9 +682,21 @@ static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
 
                cp.tx_bandwidth   = __constant_cpu_to_le32(0x00001f40);
                cp.rx_bandwidth   = __constant_cpu_to_le32(0x00001f40);
-               cp.max_latency    = __constant_cpu_to_le16(0xffff);
-               cp.content_format = cpu_to_le16(hdev->voice_setting);
-               cp.retrans_effort = 0xff;
+               cp.content_format = cpu_to_le16(setting);
+
+               switch (setting & SCO_AIRMODE_MASK) {
+               case SCO_AIRMODE_TRANSP:
+                       if (conn->pkt_type & ESCO_2EV3)
+                               cp.max_latency = __constant_cpu_to_le16(0x0008);
+                       else
+                               cp.max_latency = __constant_cpu_to_le16(0x000D);
+                       cp.retrans_effort = 0x02;
+                       break;
+               case SCO_AIRMODE_CVSD:
+                       cp.max_latency = __constant_cpu_to_le16(0xffff);
+                       cp.retrans_effort = 0xff;
+                       break;
+               }
 
                hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
                             sizeof(cp), &cp);
@@ -698,7 +713,7 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        if (sk->sk_state == BT_CONNECT2 &&
            test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
-               sco_conn_defer_accept(pi->conn->hcon, 0);
+               sco_conn_defer_accept(pi->conn->hcon, pi->setting);
                sk->sk_state = BT_CONFIG;
                msg->msg_namelen = 0;
 
@@ -714,7 +729,8 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
 {
        struct sock *sk = sock->sk;
-       int err = 0;
+       int len, err = 0;
+       struct bt_voice voice;
        u32 opt;
 
        BT_DBG("sk %p", sk);
@@ -740,6 +756,31 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
                        clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
                break;
 
+       case BT_VOICE:
+               if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
+                   sk->sk_state != BT_CONNECT2) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               voice.setting = sco_pi(sk)->setting;
+
+               len = min_t(unsigned int, sizeof(voice), optlen);
+               if (copy_from_user((char *) &voice, optval, len)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               /* Explicitly check for these values */
+               if (voice.setting != BT_VOICE_TRANSPARENT &&
+                   voice.setting != BT_VOICE_CVSD_16BIT) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               sco_pi(sk)->setting = voice.setting;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -765,7 +806,9 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
 
        switch (optname) {
        case SCO_OPTIONS:
-               if (sk->sk_state != BT_CONNECTED) {
+               if (sk->sk_state != BT_CONNECTED &&
+                   !(sk->sk_state == BT_CONNECT2 &&
+                     test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
                        err = -ENOTCONN;
                        break;
                }
@@ -781,7 +824,9 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
                break;
 
        case SCO_CONNINFO:
-               if (sk->sk_state != BT_CONNECTED) {
+               if (sk->sk_state != BT_CONNECTED &&
+                   !(sk->sk_state == BT_CONNECT2 &&
+                     test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
                        err = -ENOTCONN;
                        break;
                }
@@ -809,6 +854,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
 {
        struct sock *sk = sock->sk;
        int len, err = 0;
+       struct bt_voice voice;
 
        BT_DBG("sk %p", sk);
 
@@ -834,6 +880,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
 
                break;
 
+       case BT_VOICE:
+               voice.setting = sco_pi(sk)->setting;
+
+               len = min_t(unsigned int, len, sizeof(voice));
+               if (copy_to_user(optval, (char *)&voice, len))
+                       err = -EFAULT;
+
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
index 43dd752..2e7855a 100644 (file)
@@ -395,9 +395,13 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                rinfo->nss = ieee80211_rate_get_vht_nss(rate);
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
+
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+               brate = sband->bitrates[rate->idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
        if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
@@ -422,11 +426,13 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->mcs = sta->last_rx_rate_idx;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+               u16 brate;
 
                sband = sta->local->hw.wiphy->bands[
                                ieee80211_get_sdata_band(sta->sdata)];
-               rinfo->legacy =
-                       sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
+               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
 
        if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
@@ -856,8 +862,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                                  struct cfg80211_beacon_data *params)
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                           struct cfg80211_beacon_data *params)
 {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
@@ -1020,6 +1026,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
+       /* don't allow changing the beacon while CSA is in place - offset
+        * of channel switch counter may change
+        */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
        old = rtnl_dereference(sdata->u.ap.beacon);
        if (!old)
                return -ENOENT;
@@ -1044,6 +1056,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
                return -ENOENT;
        old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
 
+       /* abort any running channel switch */
+       sdata->vif.csa_active = false;
+       cancel_work_sync(&sdata->csa_finalize_work);
+
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                netif_carrier_off(vlan->dev);
@@ -1192,8 +1208,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                struct station_parameters *params)
 {
        int ret = 0;
-       u32 rates;
-       int i, j;
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
@@ -1286,16 +1300,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->listen_interval = params->listen_interval;
 
        if (params->supported_rates) {
-               rates = 0;
-
-               for (i = 0; i < params->supported_rates_len; i++) {
-                       int rate = (params->supported_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sta->sta.supp_rates[band] = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        sband, params->supported_rates,
+                                        params->supported_rates_len,
+                                        &sta->sta.supp_rates[band]);
        }
 
        if (params->ht_capa)
@@ -1958,18 +1966,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
        }
 
        if (params->basic_rates) {
-               int i, j;
-               u32 rates = 0;
-               struct ieee80211_supported_band *sband = wiphy->bands[band];
-
-               for (i = 0; i < params->basic_rates_len; i++) {
-                       int rate = (params->basic_rates[i] & 0x7f) * 5;
-                       for (j = 0; j < sband->n_bitrates; j++) {
-                               if (sband->bitrates[j].bitrate == rate)
-                                       rates |= BIT(j);
-                       }
-               }
-               sdata->vif.bss_conf.basic_rates = rates;
+               ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+                                        wiphy->bands[band],
+                                        params->basic_rates,
+                                        params->basic_rates_len,
+                                        &sdata->vif.bss_conf.basic_rates);
                changed |= BSS_CHANGED_BASIC_RATES;
        }
 
@@ -2301,14 +2302,25 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy)
 }
 
 #ifdef CONFIG_NL80211_TESTMODE
-static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
+static int ieee80211_testmode_cmd(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev,
+                                 void *data, int len)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_vif *vif = NULL;
 
        if (!local->ops->testmode_cmd)
                return -EOPNOTSUPP;
 
-       return local->ops->testmode_cmd(&local->hw, data, len);
+       if (wdev) {
+               struct ieee80211_sub_if_data *sdata;
+
+               sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+               if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
+                       vif = &sdata->vif;
+       }
+
+       return local->ops->testmode_cmd(&local->hw, vif, data, len);
 }
 
 static int ieee80211_testmode_dump(struct wiphy *wiphy,
@@ -2786,6 +2798,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
        return 0;
 }
 
+static struct cfg80211_beacon_data *
+cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+{
+       struct cfg80211_beacon_data *new_beacon;
+       u8 *pos;
+       int len;
+
+       len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
+             beacon->proberesp_ies_len + beacon->assocresp_ies_len +
+             beacon->probe_resp_len;
+
+       new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+       if (!new_beacon)
+               return NULL;
+
+       pos = (u8 *)(new_beacon + 1);
+       if (beacon->head_len) {
+               new_beacon->head_len = beacon->head_len;
+               new_beacon->head = pos;
+               memcpy(pos, beacon->head, beacon->head_len);
+               pos += beacon->head_len;
+       }
+       if (beacon->tail_len) {
+               new_beacon->tail_len = beacon->tail_len;
+               new_beacon->tail = pos;
+               memcpy(pos, beacon->tail, beacon->tail_len);
+               pos += beacon->tail_len;
+       }
+       if (beacon->beacon_ies_len) {
+               new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+               new_beacon->beacon_ies = pos;
+               memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+               pos += beacon->beacon_ies_len;
+       }
+       if (beacon->proberesp_ies_len) {
+               new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+               new_beacon->proberesp_ies = pos;
+               memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+               pos += beacon->proberesp_ies_len;
+       }
+       if (beacon->assocresp_ies_len) {
+               new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+               new_beacon->assocresp_ies = pos;
+               memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+               pos += beacon->assocresp_ies_len;
+       }
+       if (beacon->probe_resp_len) {
+               new_beacon->probe_resp_len = beacon->probe_resp_len;
+               beacon->probe_resp = pos;
+               memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+               pos += beacon->probe_resp_len;
+       }
+
+       return new_beacon;
+}
+
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            csa_finalize_work);
+       struct ieee80211_local *local = sdata->local;
+       int err, changed;
+
+       if (!ieee80211_sdata_running(sdata))
+               return;
+
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+               return;
+
+       sdata->radar_required = sdata->csa_radar_required;
+       err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
+                                          &changed);
+       if (WARN_ON(err < 0))
+               return;
+
+       err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+       if (err < 0)
+               return;
+
+       changed |= err;
+       kfree(sdata->u.ap.next_beacon);
+       sdata->u.ap.next_beacon = NULL;
+       sdata->vif.csa_active = false;
+
+       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+}
+
+static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                                   struct cfg80211_csa_settings *params)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       int err, num_chanctx;
+
+       if (!list_empty(&local->roc_list) || local->scanning)
+               return -EBUSY;
+
+       if (sdata->wdev.cac_started)
+               return -EBUSY;
+
+       if (cfg80211_chandef_identical(&params->chandef,
+                                      &sdata->vif.bss_conf.chandef))
+               return -EINVAL;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf) {
+               rcu_read_unlock();
+               return -EBUSY;
+       }
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1) {
+               rcu_read_unlock();
+               return -EBUSY;
+       }
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+               num_chanctx++;
+       rcu_read_unlock();
+
+       if (num_chanctx > 1)
+               return -EBUSY;
+
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
+       /* only handle AP for now. */
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
+       if (!sdata->u.ap.next_beacon)
+               return -ENOMEM;
+
+       sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
+       sdata->csa_counter_offset_presp = params->counter_offset_presp;
+       sdata->csa_radar_required = params->radar_required;
+
+       if (params->block_tx)
+               ieee80211_stop_queues_by_reason(&local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
+       err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+       if (err < 0)
+               return err;
+
+       local->csa_chandef = params->chandef;
+       sdata->vif.csa_active = true;
+
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params->chandef);
+
+       return 0;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct ieee80211_channel *chan, bool offchan,
                             unsigned int wait, const u8 *buf, size_t len,
@@ -3503,4 +3687,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
        .start_radar_detection = ieee80211_start_radar_detection,
+       .channel_switch = ieee80211_channel_switch,
 };
index 03e8d2e..3a4764b 100644 (file)
@@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
+int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+                                const struct cfg80211_chan_def *chandef,
+                                u32 *changed)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *ctx;
+       int ret;
+       u32 chanctx_changed = 0;
+
+       /* should never be called if not performing a channel switch. */
+       if (WARN_ON(!sdata->vif.csa_active))
+               return -EINVAL;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
+
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       if (ctx->refcount != 1) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+               *changed |= BSS_CHANGED_BANDWIDTH;
+       }
+
+       sdata->vif.bss_conf.chandef = *chandef;
+       ctx->conf.def = *chandef;
+
+       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+       drv_change_chanctx(local, ctx, chanctx_changed);
+
+       if (!local->use_chanctx) {
+               local->_oper_chandef = *chandef;
+               ieee80211_hw_config(local, 0);
+       }
+
+       ieee80211_recalc_chanctx_chantype(local, ctx);
+       ieee80211_recalc_smps_chanctx(local, ctx);
+       ieee80211_recalc_radar_chanctx(local, ctx);
+
+       ret = 0;
+ out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+}
+
 int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                                   const struct cfg80211_chan_def *chandef,
                                   u32 *changed)
index 44e201d..19c54a4 100644 (file)
@@ -455,6 +455,15 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
        DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
 
+       if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
+               debugfs_create_x32("driver_buffered_tids", 0400,
+                                  sta->debugfs.dir,
+                                  (u32 *)&sta->driver_buffered_tids);
+       else
+               debugfs_create_x64("driver_buffered_tids", 0400,
+                                  sta->debugfs.dir,
+                                  (u64 *)&sta->driver_buffered_tids);
+
        drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
 }
 
index b931c96..b3ea11f 100644 (file)
@@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
 }
 #endif
 
+static inline void
+drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
+                         struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       if (local->ops->channel_switch_beacon) {
+               trace_drv_channel_switch_beacon(local, sdata, chandef);
+               local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
+                                                 chandef);
+       }
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index f83534f..529bf58 100644 (file)
 #include "ieee80211_i.h"
 #include "rate.h"
 
-static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
+                                 struct ieee80211_ht_cap *ht_capa_mask,
                                  struct ieee80211_sta_ht_cap *ht_cap,
                                  u16 flag)
 {
        __le16 le_flag = cpu_to_le16(flag);
-       if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
-               if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+       if (ht_capa_mask->cap_info & le_flag) {
+               if (!(ht_capa->cap_info & le_flag))
                        ht_cap->cap &= ~flag;
        }
 }
@@ -33,13 +34,30 @@ static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_sta_ht_cap *ht_cap)
 {
-       u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
-       u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+       struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
+       u8 *scaps, *smask;
        int i;
 
        if (!ht_cap->ht_supported)
                return;
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+               ht_capa = &sdata->u.mgd.ht_capa;
+               ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ht_capa = &sdata->u.ibss.ht_capa;
+               ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       scaps = (u8 *)(&ht_capa->mcs.rx_mask);
+       smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
+
        /* NOTE:  If you add more over-rides here, update register_hw
         * ht_capa_mod_msk logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
@@ -55,28 +73,32 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        }
 
        /* Force removal of HT-40 capabilities? */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_40);
 
        /* Allow user to disable SGI-20 (SGI-40 is handled above) */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_SGI_20);
 
        /* Allow user to disable the max-AMSDU bit. */
-       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_MAX_AMSDU);
 
        /* Allow user to decrease AMPDU factor */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
-               u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
-                       & IEEE80211_HT_AMPDU_PARM_FACTOR;
+               u8 n = ht_capa->ampdu_params_info &
+                      IEEE80211_HT_AMPDU_PARM_FACTOR;
                if (n < ht_cap->ampdu_factor)
                        ht_cap->ampdu_factor = n;
        }
 
        /* Allow the user to increase AMPDU density. */
-       if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+       if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_DENSITY) {
-               u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+               u8 n = (ht_capa->ampdu_params_info &
                        IEEE80211_HT_AMPDU_PARM_DENSITY)
                        >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
                if (n > ht_cap->ampdu_density)
@@ -112,7 +134,8 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
+            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
index 2d45643..a12afe7 100644 (file)
 
 #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
 #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
+#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)
 
 #define IEEE80211_IBSS_MAX_STA_ENTRIES 128
 
-
-static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
-                                     const u8 *bssid, const int beacon_int,
-                                     struct cfg80211_chan_def *req_chandef,
-                                     const u32 basic_rates,
-                                     const u16 capability, u64 tsf,
-                                     bool creator)
+static struct beacon_data *
+ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
+                          const int beacon_int, const u32 basic_rates,
+                          const u16 capability, u64 tsf,
+                          struct cfg80211_chan_def *chandef,
+                          bool *have_higher_than_11mbit)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
-       int ratesi;
+       int rates_n = 0, i, ri;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
        struct ieee80211_supported_band *sband;
-       struct cfg80211_bss *bss;
-       u32 bss_change;
-       u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
-       struct cfg80211_chan_def chandef;
-       struct ieee80211_channel *chan;
+       u32 rate_flags, rates = 0, rates_added = 0;
        struct beacon_data *presp;
        int frame_len;
-
-       sdata_assert_lock(sdata);
-
-       /* Reset own TSF to allow time synchronization work. */
-       drv_reset_tsf(local, sdata);
-
-       if (!ether_addr_equal(ifibss->bssid, bssid))
-               sta_info_flush(sdata);
-
-       /* if merging, indicate to driver that we leave the old IBSS */
-       if (sdata->vif.bss_conf.ibss_joined) {
-               sdata->vif.bss_conf.ibss_joined = false;
-               sdata->vif.bss_conf.ibss_creator = false;
-               sdata->vif.bss_conf.enable_beacon = false;
-               netif_carrier_off(sdata->dev);
-               ieee80211_bss_info_change_notify(sdata,
-                                                BSS_CHANGED_IBSS |
-                                                BSS_CHANGED_BEACON_ENABLED);
-       }
-
-       presp = rcu_dereference_protected(ifibss->presp,
-                                         lockdep_is_held(&sdata->wdev.mtx));
-       rcu_assign_pointer(ifibss->presp, NULL);
-       if (presp)
-               kfree_rcu(presp, rcu_head);
-
-       sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
-       /* make a copy of the chandef, it could be modified below. */
-       chandef = *req_chandef;
-       chan = chandef.chan;
-       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
-               chandef.width = NL80211_CHAN_WIDTH_20;
-               chandef.center_freq1 = chan->center_freq;
-       }
-
-       ieee80211_vif_release_channel(sdata);
-       if (ieee80211_vif_use_channel(sdata, &chandef,
-                                     ifibss->fixed_channel ?
-                                       IEEE80211_CHANCTX_SHARED :
-                                       IEEE80211_CHANCTX_EXCLUSIVE)) {
-               sdata_info(sdata, "Failed to join IBSS, no channel context\n");
-               return;
-       }
-
-       memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
-       sband = local->hw.wiphy->bands[chan->band];
+       int shift;
 
        /* Build IBSS probe response */
        frame_len = sizeof(struct ieee80211_hdr_3addr) +
@@ -116,7 +65,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                    ifibss->ie_len;
        presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
        if (!presp)
-               return;
+               return NULL;
 
        presp->head = (void *)(presp + 1);
 
@@ -137,21 +86,47 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        memcpy(pos, ifibss->ssid, ifibss->ssid_len);
        pos += ifibss->ssid_len;
 
-       rates = min_t(int, 8, sband->n_bitrates);
+       sband = local->hw.wiphy->bands[chandef->chan->band];
+       rate_flags = ieee80211_chandef_rate_flags(chandef);
+       shift = ieee80211_chandef_get_shift(chandef);
+       rates_n = 0;
+       if (have_higher_than_11mbit)
+               *have_higher_than_11mbit = false;
+
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               if (sband->bitrates[i].bitrate > 110 &&
+                   have_higher_than_11mbit)
+                       *have_higher_than_11mbit = true;
+
+               rates |= BIT(i);
+               rates_n++;
+       }
+
        *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = rates;
-       for (i = 0; i < rates; i++) {
-               int rate = sband->bitrates[i].bitrate;
+       *pos++ = min_t(int, 8, rates_n);
+       for (ri = 0; ri < sband->n_bitrates; ri++) {
+               int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                       5 * (1 << shift));
                u8 basic = 0;
-               if (basic_rates & BIT(i))
+               if (!(rates & BIT(ri)))
+                       continue;
+
+               if (basic_rates & BIT(ri))
                        basic = 0x80;
-               *pos++ = basic | (u8) (rate / 5);
+               *pos++ = basic | (u8) rate;
+               if (++rates_added == 8) {
+                       ri++; /* continue at next rate for EXT_SUPP_RATES */
+                       break;
+               }
        }
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
-               *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
+               *pos++ = ieee80211_frequency_to_channel(
+                               chandef->chan->center_freq);
        }
 
        *pos++ = WLAN_EID_IBSS_PARAMS;
@@ -160,15 +135,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        *pos++ = 0;
        *pos++ = 0;
 
-       if (sband->n_bitrates > 8) {
+       /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
+       if (rates_n > 8) {
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = sband->n_bitrates - 8;
-               for (i = 8; i < sband->n_bitrates; i++) {
-                       int rate = sband->bitrates[i].bitrate;
+               *pos++ = rates_n - 8;
+               for (; ri < sband->n_bitrates; ri++) {
+                       int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+                                               5 * (1 << shift));
                        u8 basic = 0;
-                       if (basic_rates & BIT(i))
+                       if (!(rates & BIT(ri)))
+                               continue;
+
+                       if (basic_rates & BIT(ri))
                                basic = 0x80;
-                       *pos++ = basic | (u8) (rate / 5);
+                       *pos++ = basic | (u8) rate;
                }
        }
 
@@ -178,19 +158,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        /* add HT capability and information IEs */
-       if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
-           chandef.width != NL80211_CHAN_WIDTH_5 &&
-           chandef.width != NL80211_CHAN_WIDTH_10 &&
+       if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+           chandef->width != NL80211_CHAN_WIDTH_5 &&
+           chandef->width != NL80211_CHAN_WIDTH_10 &&
            sband->ht_cap.ht_supported) {
-               pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
-                                               sband->ht_cap.cap);
+               struct ieee80211_sta_ht_cap ht_cap;
+
+               memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+               ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+               pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
                /*
                 * Note: According to 802.11n-2009 9.13.3.1, HT Protection
                 * field and RIFS Mode are reserved in IBSS mode, therefore
                 * keep them at 0
                 */
                pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
-                                                &chandef, 0);
+                                                chandef, 0);
        }
 
        if (local->hw.queues >= IEEE80211_NUM_ACS) {
@@ -207,9 +191,97 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        presp->head_len = pos - presp->head;
        if (WARN_ON(presp->head_len > frame_len))
+               goto error;
+
+       return presp;
+error:
+       kfree(presp);
+       return NULL;
+}
+
+static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+                                     const u8 *bssid, const int beacon_int,
+                                     struct cfg80211_chan_def *req_chandef,
+                                     const u32 basic_rates,
+                                     const u16 capability, u64 tsf,
+                                     bool creator)
+{
+       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_mgmt *mgmt;
+       struct cfg80211_bss *bss;
+       u32 bss_change;
+       struct cfg80211_chan_def chandef;
+       struct ieee80211_channel *chan;
+       struct beacon_data *presp;
+       enum nl80211_bss_scan_width scan_width;
+       bool have_higher_than_11mbit;
+
+       sdata_assert_lock(sdata);
+
+       /* Reset own TSF to allow time synchronization work. */
+       drv_reset_tsf(local, sdata);
+
+       if (!ether_addr_equal(ifibss->bssid, bssid))
+               sta_info_flush(sdata);
+
+       /* if merging, indicate to driver that we leave the old IBSS */
+       if (sdata->vif.bss_conf.ibss_joined) {
+               sdata->vif.bss_conf.ibss_joined = false;
+               sdata->vif.bss_conf.ibss_creator = false;
+               sdata->vif.bss_conf.enable_beacon = false;
+               netif_carrier_off(sdata->dev);
+               ieee80211_bss_info_change_notify(sdata,
+                                                BSS_CHANGED_IBSS |
+                                                BSS_CHANGED_BEACON_ENABLED);
+       }
+
+       presp = rcu_dereference_protected(ifibss->presp,
+                                         lockdep_is_held(&sdata->wdev.mtx));
+       rcu_assign_pointer(ifibss->presp, NULL);
+       if (presp)
+               kfree_rcu(presp, rcu_head);
+
+       sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+
+       /* make a copy of the chandef, it could be modified below. */
+       chandef = *req_chandef;
+       chan = chandef.chan;
+       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+               if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+                   chandef.width == NL80211_CHAN_WIDTH_10 ||
+                   chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+                   chandef.width == NL80211_CHAN_WIDTH_20) {
+                       sdata_info(sdata,
+                                  "Failed to join IBSS, beacons forbidden\n");
+                       return;
+               }
+               chandef.width = NL80211_CHAN_WIDTH_20;
+               chandef.center_freq1 = chan->center_freq;
+       }
+
+       ieee80211_vif_release_channel(sdata);
+       if (ieee80211_vif_use_channel(sdata, &chandef,
+                                     ifibss->fixed_channel ?
+                                       IEEE80211_CHANCTX_SHARED :
+                                       IEEE80211_CHANCTX_EXCLUSIVE)) {
+               sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+               return;
+       }
+
+       memcpy(ifibss->bssid, bssid, ETH_ALEN);
+
+       sband = local->hw.wiphy->bands[chan->band];
+
+       presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
+                                          capability, tsf, &chandef,
+                                          &have_higher_than_11mbit);
+       if (!presp)
                return;
 
        rcu_assign_pointer(ifibss->presp, presp);
+       mgmt = (void *)presp->head;
 
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
@@ -239,18 +311,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
        bss_change |= BSS_CHANGED_ERP_SLOT;
 
+       /* cf. IEEE 802.11 9.2.12 */
+       if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+       else
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
        sdata->vif.bss_conf.ibss_joined = true;
        sdata->vif.bss_conf.ibss_creator = creator;
        ieee80211_bss_info_change_notify(sdata, bss_change);
 
-       ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
+       ieee80211_set_wmm_default(sdata, true);
 
        ifibss->state = IEEE80211_IBSS_MLME_JOINED;
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-       bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
-                                       mgmt, presp->head_len, 0, GFP_KERNEL);
+       scan_width = cfg80211_chandef_to_scan_width(&chandef);
+       bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
+                                             scan_width, mgmt,
+                                             presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
        cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
@@ -269,6 +349,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        const struct cfg80211_bss_ies *ies;
        enum nl80211_channel_type chan_type;
        u64 tsf;
+       u32 rate_flags;
+       int shift;
 
        sdata_assert_lock(sdata);
 
@@ -296,15 +378,24 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        basic_rates = 0;
 
        for (i = 0; i < bss->supp_rates_len; i++) {
-               int rate = (bss->supp_rates[i] & 0x7f) * 5;
+               int rate = bss->supp_rates[i] & 0x7f;
                bool is_basic = !!(bss->supp_rates[i] & 0x80);
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            5 * (1 << shift));
+                       if (brate == rate) {
                                if (is_basic)
                                        basic_rates |= BIT(j);
                                break;
@@ -360,6 +451,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -388,6 +480,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        if (WARN_ON_ONCE(!chanctx_conf))
                return NULL;
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
@@ -401,7 +494,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        return ieee80211_ibss_finish_sta(sta);
 }
@@ -465,6 +558,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        u64 beacon_timestamp, rx_timestamp;
        u32 supp_rates = 0;
        enum ieee80211_band band = rx_status->band;
+       enum nl80211_bss_scan_width scan_width;
        struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
        bool rates_updated = false;
 
@@ -486,16 +580,22 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                sta = sta_info_get(sdata, mgmt->sa);
 
                if (elems->supp_rates) {
-                       supp_rates = ieee80211_sta_get_rates(local, elems,
+                       supp_rates = ieee80211_sta_get_rates(sdata, elems,
                                                             band, NULL);
                        if (sta) {
                                u32 prev_rates;
 
                                prev_rates = sta->sta.supp_rates[band];
                                /* make sure mandatory rates are always added */
-                               sta->sta.supp_rates[band] = supp_rates |
-                                       ieee80211_mandatory_rates(sband);
+                               scan_width = NL80211_BSS_CHAN_WIDTH_20;
+                               if (rx_status->flag & RX_FLAG_5MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_5;
+                               if (rx_status->flag & RX_FLAG_10MHZ)
+                                       scan_width = NL80211_BSS_CHAN_WIDTH_10;
 
+                               sta->sta.supp_rates[band] = supp_rates |
+                                       ieee80211_mandatory_rates(sband,
+                                                                 scan_width);
                                if (sta->sta.supp_rates[band] != prev_rates) {
                                        ibss_dbg(sdata,
                                                 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
@@ -610,7 +710,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                         "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
                         mgmt->bssid);
                ieee80211_sta_join_ibss(sdata, bss);
-               supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
+               supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
                ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
                                       supp_rates);
                rcu_read_unlock();
@@ -629,6 +729,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
+       enum nl80211_bss_scan_width scan_width;
        int band;
 
        /*
@@ -654,6 +755,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
                return;
        }
        band = chanctx_conf->def.chan->band;
+       scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
 
        sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -665,7 +767,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.supp_rates[band] = supp_rates |
-                       ieee80211_mandatory_rates(sband);
+                       ieee80211_mandatory_rates(sband, scan_width);
 
        spin_lock(&ifibss->incomplete_lock);
        list_add(&sta->list, &ifibss->incomplete_stations);
@@ -697,6 +799,33 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
        return active;
 }
 
+static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta, *tmp;
+       unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
+       unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
+
+       mutex_lock(&local->sta_mtx);
+
+       list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+               if (sdata != sta->sdata)
+                       continue;
+
+               if (time_after(jiffies, sta->last_rx + exp_time) ||
+                   (time_after(jiffies, sta->last_rx + exp_rsn_time) &&
+                    sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
+                       sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
+                               sta->sta_state != IEEE80211_STA_AUTHORIZED ?
+                               "not authorized " : "", sta->sta.addr);
+
+                       WARN_ON(__sta_info_destroy(sta));
+               }
+       }
+
+       mutex_unlock(&local->sta_mtx);
+}
+
 /*
  * This function is called with state == IEEE80211_IBSS_MLME_JOINED
  */
@@ -704,13 +833,14 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+       enum nl80211_bss_scan_width scan_width;
 
        sdata_assert_lock(sdata);
 
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
-       ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+       ieee80211_ibss_sta_expire(sdata);
 
        if (time_before(jiffies, ifibss->last_scan_completed +
                       IEEE80211_IBSS_MERGE_INTERVAL))
@@ -725,8 +855,9 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
 
+       scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
        ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
-                                   NULL);
+                                   NULL, scan_width);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -776,6 +907,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        struct cfg80211_bss *cbss;
        struct ieee80211_channel *chan = NULL;
        const u8 *bssid = NULL;
+       enum nl80211_bss_scan_width scan_width;
        int active_ibss;
        u16 capability;
 
@@ -817,6 +949,17 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
+       /* if a fixed bssid and a fixed freq have been provided create the IBSS
+        * directly and do not waste time scanning
+        */
+       if (ifibss->fixed_bssid && ifibss->fixed_channel) {
+               sdata_info(sdata, "Created IBSS using preconfigured BSSID %pM\n",
+                          bssid);
+               ieee80211_sta_create_ibss(sdata);
+               return;
+       }
+
+
        ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n");
 
        /* Selected IBSS not found in current scan results - try to scan */
@@ -824,8 +967,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_SCAN_INTERVAL)) {
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
 
+               scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
                ieee80211_request_ibss_scan(sdata, ifibss->ssid,
-                                           ifibss->ssid_len, chan);
+                                           ifibss->ssid_len, chan,
+                                           scan_width);
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -1045,6 +1190,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params)
 {
        u32 changed = 0;
+       u32 rate_flags;
+       struct ieee80211_supported_band *sband;
+       int i;
 
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1055,6 +1203,14 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        sdata->u.ibss.privacy = params->privacy;
        sdata->u.ibss.control_port = params->control_port;
        sdata->u.ibss.basic_rates = params->basic_rates;
+
+       /* fix basic_rates if channel does not support these rates */
+       rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
+       sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       sdata->u.ibss.basic_rates &= ~BIT(i);
+       }
        memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
               sizeof(params->mcast_rate));
 
@@ -1076,6 +1232,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
        sdata->u.ibss.ssid_len = params->ssid_len;
 
+       memcpy(&sdata->u.ibss.ht_capa, &params->ht_capa,
+              sizeof(sdata->u.ibss.ht_capa));
+       memcpy(&sdata->u.ibss.ht_capa_mask, &params->ht_capa_mask,
+              sizeof(sdata->u.ibss.ht_capa_mask));
+
        /*
         * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
         * reserved, but an HT STA shall protect HT transmissions as though
@@ -1156,6 +1317,11 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        presp = rcu_dereference_protected(ifibss->presp,
                                          lockdep_is_held(&sdata->wdev.mtx));
        RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+
+       /* on the next join, re-program HT parameters */
+       memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
+       memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
+
        sdata->vif.bss_conf.ibss_joined = false;
        sdata->vif.bss_conf.ibss_creator = false;
        sdata->vif.bss_conf.enable_beacon = false;
index 8412a30..b618651 100644 (file)
@@ -53,9 +53,6 @@ struct ieee80211_local;
  * increased memory use (about 2 kB of RAM per entry). */
 #define IEEE80211_FRAGMENT_MAX 4
 
-#define TU_TO_JIFFIES(x)       (usecs_to_jiffies((x) * 1024))
-#define TU_TO_EXP_TIME(x)      (jiffies + TU_TO_JIFFIES(x))
-
 /* power level hasn't been configured (or set to automatic) */
 #define IEEE80211_UNSET_POWER_LEVEL    INT_MIN
 
@@ -259,6 +256,8 @@ struct ieee80211_if_ap {
        struct beacon_data __rcu *beacon;
        struct probe_resp __rcu *probe_resp;
 
+       /* to be used after channel switch. */
+       struct cfg80211_beacon_data *next_beacon;
        struct list_head vlans;
 
        struct ps_data ps;
@@ -509,6 +508,9 @@ struct ieee80211_if_ibss {
        /* probe response/beacon for IBSS */
        struct beacon_data __rcu *presp;
 
+       struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+       struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+
        spinlock_t incomplete_lock;
        struct list_head incomplete_stations;
 
@@ -713,6 +715,11 @@ struct ieee80211_sub_if_data {
 
        struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
 
+       struct work_struct csa_finalize_work;
+       int csa_counter_offset_beacon;
+       int csa_counter_offset_presp;
+       bool csa_radar_required;
+
        /* used to reconfigure hardware SM PS */
        struct work_struct recalc_smps;
 
@@ -809,6 +816,34 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
        return band;
 }
 
+static inline int
+ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_5:
+               return 2;
+       case NL80211_CHAN_WIDTH_10:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static inline int
+ieee80211_vif_get_shift(struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       int shift = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (chanctx_conf)
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+       rcu_read_unlock();
+
+       return shift;
+}
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
@@ -1026,7 +1061,7 @@ struct ieee80211_local {
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
        struct cfg80211_scan_request *scan_req, *hw_scan_req;
-       struct ieee80211_channel *scan_channel;
+       struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
        int scan_ies_len;
@@ -1063,7 +1098,6 @@ struct ieee80211_local {
        u32 dot11TransmittedFrameCount;
 
 #ifdef CONFIG_MAC80211_LEDS
-       int tx_led_counter, rx_led_counter;
        struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
        struct tpt_led_trigger *tpt_led_trigger;
        char tx_led_name[32], rx_led_name[32],
@@ -1306,7 +1340,8 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan);
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1341,6 +1376,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
 void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
+/* channel switch handling */
+void ieee80211_csa_finalize_work(struct work_struct *work);
+
 /* interface handling */
 int ieee80211_iface_init(void);
 void ieee80211_iface_exit(void);
@@ -1362,6 +1400,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
 
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                           struct cfg80211_beacon_data *params);
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
@@ -1465,7 +1505,8 @@ extern void *mac80211_wiphy_privid; /* for wiphy privid */
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
                        enum nl80211_iftype type);
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble);
+                            int rate, int erp, int short_preamble,
+                            int shift);
 void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
                                     struct ieee80211_hdr *hdr, const u8 *tsc,
                                     gfp_t gfp);
@@ -1569,7 +1610,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel);
+                            struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
@@ -1582,10 +1623,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              u32 ratemask, bool directed, u32 tx_flags,
                              struct ieee80211_channel *channel, bool scan);
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
 int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
@@ -1602,6 +1640,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               u16 prot_mode);
 u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
                               u32 cap);
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates);
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band);
@@ -1622,6 +1663,11 @@ int __must_check
 ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                               const struct cfg80211_chan_def *chandef,
                               u32 *changed);
+/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
+int __must_check
+ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+                            const struct cfg80211_chan_def *chandef,
+                            u32 *changed);
 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
index cc11759..7ca534b 100644 (file)
@@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
                return false;
        }
 
-       power = chanctx_conf->def.chan->max_power;
+       power = ieee80211_chandef_max_power(&chanctx_conf->def);
        rcu_read_unlock();
 
        if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
@@ -274,6 +274,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
                        if (iftype == NL80211_IFTYPE_ADHOC &&
                            nsdata->vif.type == NL80211_IFTYPE_ADHOC)
                                return -EBUSY;
+                       /*
+                        * will not add another interface while any channel
+                        * switch is active.
+                        */
+                       if (nsdata->vif.csa_active)
+                               return -EBUSY;
 
                        /*
                         * The remaining checks are only performed for interfaces
@@ -804,6 +810,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        cancel_work_sync(&sdata->recalc_smps);
+       sdata->vif.csa_active = false;
+       cancel_work_sync(&sdata->csa_finalize_work);
 
        cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
 
@@ -1267,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        skb_queue_head_init(&sdata->skb_queue);
        INIT_WORK(&sdata->work, ieee80211_iface_work);
        INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+       INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
 
        switch (type) {
        case NL80211_IFTYPE_P2P_GO:
index e39cc91..620677e 100644 (file)
@@ -93,6 +93,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 
        might_sleep();
 
+       if (key->flags & KEY_FLAG_TAINTED)
+               return -EINVAL;
+
        if (!key->local->ops->set_key)
                goto out_unsupported;
 
@@ -455,6 +458,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
                       struct ieee80211_sub_if_data *sdata,
                       struct sta_info *sta)
 {
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_key *old_key;
        int idx, ret;
        bool pairwise;
@@ -484,10 +488,13 @@ int ieee80211_key_link(struct ieee80211_key *key,
 
        ieee80211_debugfs_key_add(key);
 
-       ret = ieee80211_key_enable_hw_accel(key);
-
-       if (ret)
-               ieee80211_key_free(key, true);
+       if (!local->wowlan) {
+               ret = ieee80211_key_enable_hw_accel(key);
+               if (ret)
+                       ieee80211_key_free(key, true);
+       } else {
+               ret = 0;
+       }
 
        mutex_unlock(&sdata->local->key_mtx);
 
@@ -540,7 +547,7 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
                         void *iter_data)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_key *key;
+       struct ieee80211_key *key, *tmp;
        struct ieee80211_sub_if_data *sdata;
 
        ASSERT_RTNL();
@@ -548,13 +555,14 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
        mutex_lock(&local->key_mtx);
        if (vif) {
                sdata = vif_to_sdata(vif);
-               list_for_each_entry(key, &sdata->key_list, list)
+               list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
                        iter(hw, &sdata->vif,
                             key->sta ? &key->sta->sta : NULL,
                             &key->conf, iter_data);
        } else {
                list_for_each_entry(sdata, &local->interfaces, list)
-                       list_for_each_entry(key, &sdata->key_list, list)
+                       list_for_each_entry_safe(key, tmp,
+                                                &sdata->key_list, list)
                                iter(hw, &sdata->vif,
                                     key->sta ? &key->sta->sta : NULL,
                                     &key->conf, iter_data);
@@ -751,3 +759,135 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
        }
 }
 EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
+
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+                             struct ieee80211_key_seq *seq)
+{
+       struct ieee80211_key *key;
+       u64 pn64;
+
+       key = container_of(keyconf, struct ieee80211_key, conf);
+
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->u.tkip.tx.iv32 = seq->tkip.iv32;
+               key->u.tkip.tx.iv16 = seq->tkip.iv16;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               pn64 = (u64)seq->ccmp.pn[5] |
+                      ((u64)seq->ccmp.pn[4] << 8) |
+                      ((u64)seq->ccmp.pn[3] << 16) |
+                      ((u64)seq->ccmp.pn[2] << 24) |
+                      ((u64)seq->ccmp.pn[1] << 32) |
+                      ((u64)seq->ccmp.pn[0] << 40);
+               atomic64_set(&key->u.ccmp.tx_pn, pn64);
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               pn64 = (u64)seq->aes_cmac.pn[5] |
+                      ((u64)seq->aes_cmac.pn[4] << 8) |
+                      ((u64)seq->aes_cmac.pn[3] << 16) |
+                      ((u64)seq->aes_cmac.pn[2] << 24) |
+                      ((u64)seq->aes_cmac.pn[1] << 32) |
+                      ((u64)seq->aes_cmac.pn[0] << 40);
+               atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq);
+
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+                             int tid, struct ieee80211_key_seq *seq)
+{
+       struct ieee80211_key *key;
+       u8 *pn;
+
+       key = container_of(keyconf, struct ieee80211_key, conf);
+
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
+                       return;
+               key->u.tkip.rx[tid].iv32 = seq->tkip.iv32;
+               key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+                       return;
+               if (tid < 0)
+                       pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
+               else
+                       pn = key->u.ccmp.rx_pn[tid];
+               memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               if (WARN_ON(tid != 0))
+                       return;
+               pn = key->u.aes_cmac.rx_pn;
+               memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq);
+
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf)
+{
+       struct ieee80211_key *key;
+
+       key = container_of(keyconf, struct ieee80211_key, conf);
+
+       assert_key_lock(key->local);
+
+       /*
+        * if key was uploaded, we assume the driver will/has remove(d)
+        * it, so adjust bookkeeping accordingly
+        */
+       if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
+               key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+               if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+                     (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
+                     (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
+                       increment_tailroom_need_count(key->sdata);
+       }
+
+       ieee80211_key_free(key, false);
+}
+EXPORT_SYMBOL_GPL(ieee80211_remove_key);
+
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+                       struct ieee80211_key_conf *keyconf)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_key *key;
+       int err;
+
+       if (WARN_ON(!local->wowlan))
+               return ERR_PTR(-EINVAL);
+
+       if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+               return ERR_PTR(-EINVAL);
+
+       key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
+                                 keyconf->keylen, keyconf->key,
+                                 0, NULL);
+       if (IS_ERR(key))
+               return ERR_PTR(PTR_ERR(key));
+
+       if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+               key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+
+       err = ieee80211_key_link(key, sdata, NULL);
+       if (err)
+               return ERR_PTR(err);
+
+       return &key->conf;
+}
+EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add);
index bcffa69..e2b8364 100644 (file)
 #include <linux/export.h>
 #include "led.h"
 
+#define MAC80211_BLINK_DELAY 50 /* ms */
+
 void ieee80211_led_rx(struct ieee80211_local *local)
 {
+       unsigned long led_delay = MAC80211_BLINK_DELAY;
        if (unlikely(!local->rx_led))
                return;
-       if (local->rx_led_counter++ % 2 == 0)
-               led_trigger_event(local->rx_led, LED_OFF);
-       else
-               led_trigger_event(local->rx_led, LED_FULL);
+       led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0);
 }
 
-/* q is 1 if a packet was enqueued, 0 if it has been transmitted */
-void ieee80211_led_tx(struct ieee80211_local *local, int q)
+void ieee80211_led_tx(struct ieee80211_local *local)
 {
+       unsigned long led_delay = MAC80211_BLINK_DELAY;
        if (unlikely(!local->tx_led))
                return;
-       /* not sure how this is supposed to work ... */
-       local->tx_led_counter += 2*q-1;
-       if (local->tx_led_counter % 2 == 0)
-               led_trigger_event(local->tx_led, LED_OFF);
-       else
-               led_trigger_event(local->tx_led, LED_FULL);
+       led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0);
 }
 
 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
index e0275d9..89f4344 100644 (file)
@@ -13,7 +13,7 @@
 
 #ifdef CONFIG_MAC80211_LEDS
 void ieee80211_led_rx(struct ieee80211_local *local);
-void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_tx(struct ieee80211_local *local);
 void ieee80211_led_assoc(struct ieee80211_local *local,
                         bool associated);
 void ieee80211_led_radio(struct ieee80211_local *local,
@@ -27,7 +27,7 @@ void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
 static inline void ieee80211_led_rx(struct ieee80211_local *local)
 {
 }
-static inline void ieee80211_led_tx(struct ieee80211_local *local, int q)
+static inline void ieee80211_led_tx(struct ieee80211_local *local)
 {
 }
 static inline void ieee80211_led_assoc(struct ieee80211_local *local,
index 091088a..25eb35b 100644 (file)
@@ -102,17 +102,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
 
        offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
 
-       if (local->scan_channel) {
-               chandef.chan = local->scan_channel;
-               /* If scanning on oper channel, use whatever channel-type
-                * is currently in use.
-                */
-               if (chandef.chan == local->_oper_chandef.chan) {
-                       chandef = local->_oper_chandef;
-               } else {
-                       chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
-                       chandef.center_freq1 = chandef.chan->center_freq;
-               }
+       if (local->scan_chandef.chan) {
+               chandef = local->scan_chandef;
        } else if (local->tmp_channel) {
                chandef.chan = local->tmp_channel;
                chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -151,7 +142,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
                changed |= IEEE80211_CONF_CHANGE_SMPS;
        }
 
-       power = chandef.chan->max_power;
+       power = ieee80211_chandef_max_power(&chandef);
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
index 447f41b..885a5f6 100644 (file)
@@ -62,7 +62,6 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
                        struct ieee802_11_elems *ie)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-       struct ieee80211_local *local = sdata->local;
        u32 basic_rates = 0;
        struct cfg80211_chan_def sta_chan_def;
 
@@ -85,7 +84,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
             (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
                return false;
 
-       ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
+       ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
                                &basic_rates);
 
        if (sdata->vif.bss_conf.basic_rates != basic_rates)
@@ -274,7 +273,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
-       *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
+       *pos = 0x00;
+       *pos |= ifmsh->mshcfg.dot11MeshForwarding ?
+                       IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
        *pos |= ifmsh->accepting_plinks ?
                        IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
        /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
index 02c05fa..6b65d50 100644 (file)
@@ -379,7 +379,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
        u32 rates, basic_rates = 0, changed = 0;
 
        sband = local->hw.wiphy->bands[band];
-       rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
+       rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
 
        spin_lock_bh(&sta->lock);
        sta->last_rx = jiffies;
index cc9e02d..2aab130 100644 (file)
@@ -489,27 +489,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
 
 /* frame sending functions */
 
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
-                                     struct ieee80211_supported_band *sband,
-                                     u32 *rates)
-{
-       int i, j, count;
-       *rates = 0;
-       count = 0;
-       for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7F) * 5;
-
-               for (j = 0; j < sband->n_bitrates; j++)
-                       if (sband->bitrates[j].bitrate == rate) {
-                               *rates |= BIT(j);
-                               count++;
-                               break;
-                       }
-       }
-
-       return count;
-}
-
 static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, u8 ap_ht_param,
                                struct ieee80211_supported_band *sband,
@@ -628,12 +607,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_mgmt *mgmt;
        u8 *pos, qos_info;
        size_t offset = 0, noffset;
-       int i, count, rates_len, supp_rates_len;
+       int i, count, rates_len, supp_rates_len, shift;
        u16 capab;
        struct ieee80211_supported_band *sband;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *chan;
-       u32 rates = 0;
+       u32 rate_flags, rates = 0;
 
        sdata_assert_lock(sdata);
 
@@ -644,8 +623,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                return;
        }
        chan = chanctx_conf->def.chan;
+       rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
        rcu_read_unlock();
        sband = local->hw.wiphy->bands[chan->band];
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (assoc_data->supp_rates_len) {
                /*
@@ -654,17 +635,24 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                 * in the association request (e.g. D-Link DAP 1353 in
                 * b-only mode)...
                 */
-               rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
-                                                      assoc_data->supp_rates_len,
-                                                      sband, &rates);
+               rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
+                                                    assoc_data->supp_rates,
+                                                    assoc_data->supp_rates_len,
+                                                    &rates);
        } else {
                /*
                 * In case AP not provide any supported rates information
                 * before association, we send information element(s) with
                 * all rates that we support.
                 */
-               rates = ~0;
-               rates_len = sband->n_bitrates;
+               rates_len = 0;
+               for (i = 0; i < sband->n_bitrates; i++) {
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       rates |= BIT(i);
+                       rates_len++;
+               }
        }
 
        skb = alloc_skb(local->hw.extra_tx_headroom +
@@ -741,8 +729,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        count = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if (BIT(i) & rates) {
-                       int rate = sband->bitrates[i].bitrate;
-                       *pos++ = (u8) (rate / 5);
+                       int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                               5 * (1 << shift));
+                       *pos++ = (u8) rate;
                        if (++count == 8)
                                break;
                }
@@ -755,8 +744,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 
                for (i++; i < sband->n_bitrates; i++) {
                        if (BIT(i) & rates) {
-                               int rate = sband->bitrates[i].bitrate;
-                               *pos++ = (u8) (rate / 5);
+                               int rate;
+                               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                                   5 * (1 << shift));
+                               *pos++ = (u8) rate;
                        }
                }
        }
@@ -767,7 +758,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                *pos++ = WLAN_EID_PWR_CAPABILITY;
                *pos++ = 2;
                *pos++ = 0; /* min tx power */
-               *pos++ = chan->max_power; /* max tx power */
+                /* max tx power */
+               *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
 
                /* 2. supported channels */
                /* TODO: get this in reg domain format */
@@ -1121,6 +1113,15 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        case -1:
                cfg80211_chandef_create(&new_chandef, new_chan,
                                        NL80211_CHAN_NO_HT);
+               /* keep width for 5/10 MHz channels */
+               switch (sdata->vif.bss_conf.chandef.width) {
+               case NL80211_CHAN_WIDTH_5:
+               case NL80211_CHAN_WIDTH_10:
+                       new_chandef.width = sdata->vif.bss_conf.chandef.width;
+                       break;
+               default:
+                       break;
+               }
                break;
        }
 
@@ -2443,15 +2444,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                                u8 *supp_rates, unsigned int supp_rates_len,
                                u32 *rates, u32 *basic_rates,
                                bool *have_higher_than_11mbit,
-                               int *min_rate, int *min_rate_index)
+                               int *min_rate, int *min_rate_index,
+                               int shift, u32 rate_flags)
 {
        int i, j;
 
        for (i = 0; i < supp_rates_len; i++) {
-               int rate = (supp_rates[i] & 0x7f) * 5;
+               int rate = supp_rates[i] & 0x7f;
                bool is_basic = !!(supp_rates[i] & 0x80);
 
-               if (rate > 110)
+               if ((rate * 5 * (1 << shift)) > 110)
                        *have_higher_than_11mbit = true;
 
                /*
@@ -2467,12 +2469,20 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
                        continue;
 
                for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
+                       struct ieee80211_rate *br;
+                       int brate;
+
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
                                *rates |= BIT(j);
                                if (is_basic)
                                        *basic_rates |= BIT(j);
-                               if (rate < *min_rate) {
-                                       *min_rate = rate;
+                               if ((rate * 5) < *min_rate) {
+                                       *min_rate = rate * 5;
                                        *min_rate_index = j;
                                }
                                break;
@@ -3902,27 +3912,40 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                if (!new_sta)
                        return -ENOMEM;
        }
-
        if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
+               struct ieee80211_chanctx_conf *chanctx_conf;
                struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
+               int shift;
+               u32 rate_flags;
 
                sband = local->hw.wiphy->bands[cbss->channel->band];
 
                err = ieee80211_prep_channel(sdata, cbss);
                if (err) {
                        sta_info_free(local, new_sta);
-                       return err;
+                       return -EINVAL;
                }
+               shift = ieee80211_vif_get_shift(&sdata->vif);
+
+               rcu_read_lock();
+               chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+               rcu_read_unlock();
 
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                                    &rates, &basic_rates,
                                    &have_higher_than_11mbit,
-                                   &min_rate, &min_rate_index);
+                                   &min_rate, &min_rate_index,
+                                   shift, rate_flags);
 
                /*
                 * This used to be a workaround for basic rates missing
index 30d58d2..e126605 100644 (file)
@@ -210,7 +210,7 @@ static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
                !ieee80211_is_data(fc);
 }
 
-static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
+static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
                                  struct ieee80211_supported_band *sband)
 {
        u8 i;
@@ -232,37 +232,28 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
        /* could not find a basic rate; use original selection */
 }
 
-static inline s8
-rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta)
+static void __rate_control_send_low(struct ieee80211_hw *hw,
+                                   struct ieee80211_supported_band *sband,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_tx_info *info)
 {
        int i;
+       u32 rate_flags =
+               ieee80211_chandef_rate_flags(&hw->conf.chandef);
+
+       if ((sband->band == IEEE80211_BAND_2GHZ) &&
+           (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+               rate_flags |= IEEE80211_RATE_ERP_G;
 
+       info->control.rates[0].idx = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *srate = &sband->bitrates[i];
-               if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
-                   (srate->bitrate == 55) || (srate->bitrate == 110))
+               if (!rate_supported(sta, sband->band, i))
                        continue;
 
-               if (rate_supported(sta, sband->band, i))
-                       return i;
+               info->control.rates[0].idx = i;
+               break;
        }
-
-       /* No matching rate found */
-       return 0;
-}
-
-static void __rate_control_send_low(struct ieee80211_hw *hw,
-                                   struct ieee80211_supported_band *sband,
-                                   struct ieee80211_sta *sta,
-                                   struct ieee80211_tx_info *info)
-{
-       if ((sband->band != IEEE80211_BAND_2GHZ) ||
-           !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
-               info->control.rates[0].idx = rate_lowest_index(sband, sta);
-       else
-               info->control.rates[0].idx =
-                       rate_lowest_non_cck_index(sband, sta);
+       WARN_ON_ONCE(i == sband->n_bitrates);
 
        info->control.rates[0].count =
                (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
@@ -272,28 +263,37 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
 }
 
 
-bool rate_control_send_low(struct ieee80211_sta *sta,
+bool rate_control_send_low(struct ieee80211_sta *pubsta,
                           void *priv_sta,
                           struct ieee80211_tx_rate_control *txrc)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
        struct ieee80211_supported_band *sband = txrc->sband;
+       struct sta_info *sta;
        int mcast_rate;
+       bool use_basicrate = false;
 
-       if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
-               __rate_control_send_low(txrc->hw, sband, sta, info);
+       if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
+               __rate_control_send_low(txrc->hw, sband, pubsta, info);
 
-               if (!sta && txrc->bss) {
+               if (!pubsta && txrc->bss) {
                        mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
                        if (mcast_rate > 0) {
                                info->control.rates[0].idx = mcast_rate - 1;
                                return true;
                        }
+                       use_basicrate = true;
+               } else if (pubsta) {
+                       sta = container_of(pubsta, struct sta_info, sta);
+                       if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+                               use_basicrate = true;
+               }
 
-                       rc_send_low_broadcast(&info->control.rates[0].idx,
+               if (use_basicrate)
+                       rc_send_low_basicrate(&info->control.rates[0].idx,
                                              txrc->bss_conf->basic_rates,
                                              sband);
-               }
+
                return true;
        }
        return false;
@@ -585,6 +585,7 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
        u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
        bool has_mcs_mask;
        u32 mask;
+       u32 rate_flags;
        int i;
 
        /*
@@ -594,6 +595,12 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
         */
        mask = sdata->rc_rateidx_mask[info->band];
        has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
+       rate_flags =
+               ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       for (i = 0; i < sband->n_bitrates; i++)
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       mask &= ~BIT(i);
+
        if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
                return;
 
index d35a5dd..5dedc56 100644 (file)
@@ -66,11 +66,12 @@ static inline void rate_control_rate_init(struct sta_info *sta)
        }
 
        sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
-       rcu_read_unlock();
 
        ieee80211_sta_set_rx_nss(sta);
 
-       ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
+       ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+                           priv_sta);
+       rcu_read_unlock();
        set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
 }
 
@@ -81,10 +82,21 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
        struct rate_control_ref *ref = local->rate_ctrl;
        struct ieee80211_sta *ista = &sta->sta;
        void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       if (ref && ref->ops->rate_update) {
+               rcu_read_lock();
 
-       if (ref && ref->ops->rate_update)
-               ref->ops->rate_update(ref->priv, sband, ista,
-                                     priv_sta, changed);
+               chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+               if (WARN_ON(!chanctx_conf)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+                                     ista, priv_sta, changed);
+               rcu_read_unlock();
+       }
        drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
 }
 
index e6512e2..8b5f7ef 100644 (file)
@@ -383,14 +383,18 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
 static void
 calc_rate_durations(enum ieee80211_band band,
                    struct minstrel_rate *d,
-                   struct ieee80211_rate *rate)
+                   struct ieee80211_rate *rate,
+                   struct cfg80211_chan_def *chandef)
 {
        int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+       int shift = ieee80211_chandef_get_shift(chandef);
 
        d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
        d->ack_time = ieee80211_frame_duration(band, 10,
-                       rate->bitrate, erp, 1);
+                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+                       shift);
 }
 
 static void
@@ -418,21 +422,25 @@ init_sample_table(struct minstrel_sta_info *mi)
 
 static void
 minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
-               struct ieee80211_sta *sta, void *priv_sta)
+                  struct cfg80211_chan_def *chandef,
+                  struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_sta_info *mi = priv_sta;
        struct minstrel_priv *mp = priv;
        struct ieee80211_rate *ctl_rate;
        unsigned int i, n = 0;
        unsigned int t_slot = 9; /* FIXME: get real slot time */
+       u32 rate_flags;
 
        mi->sta = sta;
        mi->lowest_rix = rate_lowest_index(sband, sta);
        ctl_rate = &sband->bitrates[mi->lowest_rix];
        mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
                                ctl_rate->bitrate,
-                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+                               ieee80211_chandef_get_shift(chandef));
 
+       rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
        mi->max_prob_rate = 0;
 
@@ -441,15 +449,22 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
                unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
                unsigned int tx_time_single;
                unsigned int cw = mp->cw_min;
+               int shift;
 
                if (!rate_supported(sta, sband->band, i))
                        continue;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                n++;
                memset(mr, 0, sizeof(*mr));
 
                mr->rix = i;
-               mr->bitrate = sband->bitrates[i].bitrate / 5;
-               calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+               shift = ieee80211_chandef_get_shift(chandef);
+               mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                          (1 << shift) * 5);
+               calc_rate_durations(sband->band, mr, &sband->bitrates[i],
+                                   chandef);
 
                /* calculate maximum number of retransmissions before
                 * fallback (based on maximum segment size) */
@@ -547,6 +562,7 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
 {
        static const int bitrates[4] = { 10, 20, 55, 110 };
        struct ieee80211_supported_band *sband;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        int i, j;
 
        sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -559,6 +575,9 @@ minstrel_init_cck_rates(struct minstrel_priv *mp)
                if (rate->flags & IEEE80211_RATE_ERP_G)
                        continue;
 
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
                        if (rate->bitrate != bitrates[j])
                                continue;
index f3bbea1..339a6b8 100644 (file)
@@ -439,12 +439,13 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        u16 tid;
 
        if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
                return;
 
-       if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
+       if (unlikely(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
                return;
 
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
@@ -776,7 +777,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
 
        /* Don't use EAPOL frames for sampling on non-mrr hw */
        if (mp->hw->max_rates == 1 &&
-           txrc->skb->protocol == cpu_to_be16(ETH_P_PAE))
+           (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
                sample_idx = -1;
        else
                sample_idx = minstrel_get_sample_rate(mp, mi);
@@ -847,6 +848,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_priv *mp = priv;
@@ -872,8 +874,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        mi->sta = sta;
        mi->stats_update = jiffies;
 
-       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
-       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+       ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
+       mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
+       mi->overhead += ack_dur;
        mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
 
        mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
@@ -942,22 +945,25 @@ use_legacy:
        memset(&msp->legacy, 0, sizeof(msp->legacy));
        msp->legacy.r = msp->ratelist;
        msp->legacy.sample_table = msp->sample_table;
-       return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
+       return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
+                                          &msp->legacy);
 }
 
 static void
 minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                     struct cfg80211_chan_def *chandef,
                       struct ieee80211_sta *sta, void *priv_sta)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void
 minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
+                       struct cfg80211_chan_def *chandef,
                         struct ieee80211_sta *sta, void *priv_sta,
                         u32 changed)
 {
-       minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+       minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
 }
 
 static void *
index 502d3ec..958fad0 100644 (file)
@@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 
 static void
 rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
+                          struct cfg80211_chan_def *chandef,
                           struct ieee80211_sta *sta, void *priv_sta)
 {
        struct rc_pid_sta_info *spinfo = priv_sta;
index 2c5a79b..a84f319 100644 (file)
@@ -87,11 +87,13 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        int len;
 
        /* always present fields */
-       len = sizeof(struct ieee80211_radiotap_header) + 9;
+       len = sizeof(struct ieee80211_radiotap_header) + 8;
 
-       /* allocate extra bitmap */
+       /* allocate extra bitmaps */
        if (status->vendor_radiotap_len)
                len += 4;
+       if (status->chains)
+               len += 4 * hweight8(status->chains);
 
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
@@ -100,6 +102,10 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                len += 1;
 
+       /* antenna field, if we don't have per-chain info */
+       if (!status->chains)
+               len += 1;
+
        /* padding for RX_FLAGS if necessary */
        len = ALIGN(len, 2);
 
@@ -116,6 +122,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
                len += 12;
        }
 
+       if (status->chains) {
+               /* antenna and antenna signal fields */
+               len += 2 * hweight8(status->chains);
+       }
+
        if (status->vendor_radiotap_len) {
                if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
                        status->vendor_radiotap_align = 1;
@@ -145,8 +156,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
+       __le32 *it_present;
+       u32 it_present_val;
        u16 rx_flags = 0;
-       int mpdulen;
+       u16 channel_flags = 0;
+       int mpdulen, chain;
+       unsigned long chains = status->chains;
 
        mpdulen = skb->len;
        if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@@ -154,25 +169,39 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
        memset(rthdr, 0, rtap_len);
+       it_present = &rthdr->it_present;
 
        /* radiotap header, set always present flags */
-       rthdr->it_present =
-               cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-                           (1 << IEEE80211_RADIOTAP_CHANNEL) |
-                           (1 << IEEE80211_RADIOTAP_ANTENNA) |
-                           (1 << IEEE80211_RADIOTAP_RX_FLAGS));
        rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+       it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+                        BIT(IEEE80211_RADIOTAP_CHANNEL) |
+                        BIT(IEEE80211_RADIOTAP_RX_FLAGS);
+
+       if (!status->chains)
+               it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
 
-       pos = (unsigned char *)(rthdr + 1);
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               it_present_val |=
+                       BIT(IEEE80211_RADIOTAP_EXT) |
+                       BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                                BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+       }
 
        if (status->vendor_radiotap_len) {
-               rthdr->it_present |=
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
-               put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
-               pos += 4;
+               it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                                 BIT(IEEE80211_RADIOTAP_EXT);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = status->vendor_radiotap_bitmap;
        }
 
+       put_unaligned_le32(it_present_val, it_present);
+
+       pos = (void *)(it_present + 1);
+
        /* the order of the following fields is important */
 
        /* IEEE80211_RADIOTAP_TSFT */
@@ -207,28 +236,35 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                 */
                *pos = 0;
        } else {
+               int shift = 0;
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = rate->bitrate / 5;
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               else if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
+               *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
        }
        pos++;
 
        /* IEEE80211_RADIOTAP_CHANNEL */
        put_unaligned_le16(status->freq, pos);
        pos += 2;
+       if (status->flag & RX_FLAG_10MHZ)
+               channel_flags |= IEEE80211_CHAN_HALF;
+       else if (status->flag & RX_FLAG_5MHZ)
+               channel_flags |= IEEE80211_CHAN_QUARTER;
+
        if (status->band == IEEE80211_BAND_5GHZ)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
        else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
-               put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
        else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else if (rate)
-               put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else
-               put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+               channel_flags |= IEEE80211_CHAN_2GHZ;
+       put_unaligned_le16(channel_flags, pos);
        pos += 2;
 
        /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -242,9 +278,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
 
-       /* IEEE80211_RADIOTAP_ANTENNA */
-       *pos = status->antenna;
-       pos++;
+       if (!status->chains) {
+               /* IEEE80211_RADIOTAP_ANTENNA */
+               *pos = status->antenna;
+               pos++;
+       }
 
        /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
@@ -341,6 +379,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                pos += 2;
        }
 
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               *pos++ = status->chain_signal[chain];
+               *pos++ = chain;
+       }
+
        if (status->vendor_radiotap_len) {
                /* ensure 2 byte alignment for the vendor field as required */
                if ((pos - (u8 *)rthdr) & 1)
@@ -1012,207 +1055,6 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
 
 
 static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
-{
-       struct sk_buff *skb = rx->skb;
-       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       int keyidx;
-       int hdrlen;
-       ieee80211_rx_result result = RX_DROP_UNUSABLE;
-       struct ieee80211_key *sta_ptk = NULL;
-       int mmie_keyidx = -1;
-       __le16 fc;
-
-       /*
-        * Key selection 101
-        *
-        * There are four types of keys:
-        *  - GTK (group keys)
-        *  - IGTK (group keys for management frames)
-        *  - PTK (pairwise keys)
-        *  - STK (station-to-station pairwise keys)
-        *
-        * When selecting a key, we have to distinguish between multicast
-        * (including broadcast) and unicast frames, the latter can only
-        * use PTKs and STKs while the former always use GTKs and IGTKs.
-        * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
-        * unicast frames can also use key indices like GTKs. Hence, if we
-        * don't have a PTK/STK we check the key index for a WEP key.
-        *
-        * Note that in a regular BSS, multicast frames are sent by the
-        * AP only, associated stations unicast the frame to the AP first
-        * which then multicasts it on their behalf.
-        *
-        * There is also a slight problem in IBSS mode: GTKs are negotiated
-        * with each station, that is something we don't currently handle.
-        * The spec seems to expect that one negotiates the same key with
-        * every station but there's no such requirement; VLANs could be
-        * possible.
-        */
-
-       /*
-        * No point in finding a key and decrypting if the frame is neither
-        * addressed to us nor a multicast frame.
-        */
-       if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
-               return RX_CONTINUE;
-
-       /* start without a key */
-       rx->key = NULL;
-
-       if (rx->sta)
-               sta_ptk = rcu_dereference(rx->sta->ptk);
-
-       fc = hdr->frame_control;
-
-       if (!ieee80211_has_protected(fc))
-               mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
-
-       if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
-               rx->key = sta_ptk;
-               if ((status->flag & RX_FLAG_DECRYPTED) &&
-                   (status->flag & RX_FLAG_IV_STRIPPED))
-                       return RX_CONTINUE;
-               /* Skip decryption if the frame is not protected. */
-               if (!ieee80211_has_protected(fc))
-                       return RX_CONTINUE;
-       } else if (mmie_keyidx >= 0) {
-               /* Broadcast/multicast robust management frame / BIP */
-               if ((status->flag & RX_FLAG_DECRYPTED) &&
-                   (status->flag & RX_FLAG_IV_STRIPPED))
-                       return RX_CONTINUE;
-
-               if (mmie_keyidx < NUM_DEFAULT_KEYS ||
-                   mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
-                       return RX_DROP_MONITOR; /* unexpected BIP keyidx */
-               if (rx->sta)
-                       rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
-               if (!rx->key)
-                       rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
-       } else if (!ieee80211_has_protected(fc)) {
-               /*
-                * The frame was not protected, so skip decryption. However, we
-                * need to set rx->key if there is a key that could have been
-                * used so that the frame may be dropped if encryption would
-                * have been expected.
-                */
-               struct ieee80211_key *key = NULL;
-               struct ieee80211_sub_if_data *sdata = rx->sdata;
-               int i;
-
-               if (ieee80211_is_mgmt(fc) &&
-                   is_multicast_ether_addr(hdr->addr1) &&
-                   (key = rcu_dereference(rx->sdata->default_mgmt_key)))
-                       rx->key = key;
-               else {
-                       if (rx->sta) {
-                               for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-                                       key = rcu_dereference(rx->sta->gtk[i]);
-                                       if (key)
-                                               break;
-                               }
-                       }
-                       if (!key) {
-                               for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-                                       key = rcu_dereference(sdata->keys[i]);
-                                       if (key)
-                                               break;
-                               }
-                       }
-                       if (key)
-                               rx->key = key;
-               }
-               return RX_CONTINUE;
-       } else {
-               u8 keyid;
-               /*
-                * The device doesn't give us the IV so we won't be
-                * able to look up the key. That's ok though, we
-                * don't need to decrypt the frame, we just won't
-                * be able to keep statistics accurate.
-                * Except for key threshold notifications, should
-                * we somehow allow the driver to tell us which key
-                * the hardware used if this flag is set?
-                */
-               if ((status->flag & RX_FLAG_DECRYPTED) &&
-                   (status->flag & RX_FLAG_IV_STRIPPED))
-                       return RX_CONTINUE;
-
-               hdrlen = ieee80211_hdrlen(fc);
-
-               if (rx->skb->len < 8 + hdrlen)
-                       return RX_DROP_UNUSABLE; /* TODO: count this? */
-
-               /*
-                * no need to call ieee80211_wep_get_keyidx,
-                * it verifies a bunch of things we've done already
-                */
-               skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
-               keyidx = keyid >> 6;
-
-               /* check per-station GTK first, if multicast packet */
-               if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
-                       rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
-
-               /* if not found, try default key */
-               if (!rx->key) {
-                       rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
-
-                       /*
-                        * RSNA-protected unicast frames should always be
-                        * sent with pairwise or station-to-station keys,
-                        * but for WEP we allow using a key index as well.
-                        */
-                       if (rx->key &&
-                           rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
-                           rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
-                           !is_multicast_ether_addr(hdr->addr1))
-                               rx->key = NULL;
-               }
-       }
-
-       if (rx->key) {
-               if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
-                       return RX_DROP_MONITOR;
-
-               rx->key->tx_rx_count++;
-               /* TODO: add threshold stuff again */
-       } else {
-               return RX_DROP_MONITOR;
-       }
-
-       switch (rx->key->conf.cipher) {
-       case WLAN_CIPHER_SUITE_WEP40:
-       case WLAN_CIPHER_SUITE_WEP104:
-               result = ieee80211_crypto_wep_decrypt(rx);
-               break;
-       case WLAN_CIPHER_SUITE_TKIP:
-               result = ieee80211_crypto_tkip_decrypt(rx);
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               result = ieee80211_crypto_ccmp_decrypt(rx);
-               break;
-       case WLAN_CIPHER_SUITE_AES_CMAC:
-               result = ieee80211_crypto_aes_cmac_decrypt(rx);
-               break;
-       default:
-               /*
-                * We can reach here only with HW-only algorithms
-                * but why didn't it decrypt the frame?!
-                */
-               return RX_DROP_UNUSABLE;
-       }
-
-       /* the hdr variable is invalid after the decrypt handlers */
-
-       /* either the frame has been decrypted or will be dropped */
-       status->flag |= RX_FLAG_DECRYPTED;
-
-       return result;
-}
-
-static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_local *local;
@@ -1513,6 +1355,207 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 } /* ieee80211_rx_h_sta_process */
 
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
+{
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int keyidx;
+       int hdrlen;
+       ieee80211_rx_result result = RX_DROP_UNUSABLE;
+       struct ieee80211_key *sta_ptk = NULL;
+       int mmie_keyidx = -1;
+       __le16 fc;
+
+       /*
+        * Key selection 101
+        *
+        * There are four types of keys:
+        *  - GTK (group keys)
+        *  - IGTK (group keys for management frames)
+        *  - PTK (pairwise keys)
+        *  - STK (station-to-station pairwise keys)
+        *
+        * When selecting a key, we have to distinguish between multicast
+        * (including broadcast) and unicast frames, the latter can only
+        * use PTKs and STKs while the former always use GTKs and IGTKs.
+        * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
+        * unicast frames can also use key indices like GTKs. Hence, if we
+        * don't have a PTK/STK we check the key index for a WEP key.
+        *
+        * Note that in a regular BSS, multicast frames are sent by the
+        * AP only, associated stations unicast the frame to the AP first
+        * which then multicasts it on their behalf.
+        *
+        * There is also a slight problem in IBSS mode: GTKs are negotiated
+        * with each station, that is something we don't currently handle.
+        * The spec seems to expect that one negotiates the same key with
+        * every station but there's no such requirement; VLANs could be
+        * possible.
+        */
+
+       /*
+        * No point in finding a key and decrypting if the frame is neither
+        * addressed to us nor a multicast frame.
+        */
+       if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+               return RX_CONTINUE;
+
+       /* start without a key */
+       rx->key = NULL;
+
+       if (rx->sta)
+               sta_ptk = rcu_dereference(rx->sta->ptk);
+
+       fc = hdr->frame_control;
+
+       if (!ieee80211_has_protected(fc))
+               mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
+
+       if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
+               rx->key = sta_ptk;
+               if ((status->flag & RX_FLAG_DECRYPTED) &&
+                   (status->flag & RX_FLAG_IV_STRIPPED))
+                       return RX_CONTINUE;
+               /* Skip decryption if the frame is not protected. */
+               if (!ieee80211_has_protected(fc))
+                       return RX_CONTINUE;
+       } else if (mmie_keyidx >= 0) {
+               /* Broadcast/multicast robust management frame / BIP */
+               if ((status->flag & RX_FLAG_DECRYPTED) &&
+                   (status->flag & RX_FLAG_IV_STRIPPED))
+                       return RX_CONTINUE;
+
+               if (mmie_keyidx < NUM_DEFAULT_KEYS ||
+                   mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
+                       return RX_DROP_MONITOR; /* unexpected BIP keyidx */
+               if (rx->sta)
+                       rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+               if (!rx->key)
+                       rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
+       } else if (!ieee80211_has_protected(fc)) {
+               /*
+                * The frame was not protected, so skip decryption. However, we
+                * need to set rx->key if there is a key that could have been
+                * used so that the frame may be dropped if encryption would
+                * have been expected.
+                */
+               struct ieee80211_key *key = NULL;
+               struct ieee80211_sub_if_data *sdata = rx->sdata;
+               int i;
+
+               if (ieee80211_is_mgmt(fc) &&
+                   is_multicast_ether_addr(hdr->addr1) &&
+                   (key = rcu_dereference(rx->sdata->default_mgmt_key)))
+                       rx->key = key;
+               else {
+                       if (rx->sta) {
+                               for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+                                       key = rcu_dereference(rx->sta->gtk[i]);
+                                       if (key)
+                                               break;
+                               }
+                       }
+                       if (!key) {
+                               for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+                                       key = rcu_dereference(sdata->keys[i]);
+                                       if (key)
+                                               break;
+                               }
+                       }
+                       if (key)
+                               rx->key = key;
+               }
+               return RX_CONTINUE;
+       } else {
+               u8 keyid;
+               /*
+                * The device doesn't give us the IV so we won't be
+                * able to look up the key. That's ok though, we
+                * don't need to decrypt the frame, we just won't
+                * be able to keep statistics accurate.
+                * Except for key threshold notifications, should
+                * we somehow allow the driver to tell us which key
+                * the hardware used if this flag is set?
+                */
+               if ((status->flag & RX_FLAG_DECRYPTED) &&
+                   (status->flag & RX_FLAG_IV_STRIPPED))
+                       return RX_CONTINUE;
+
+               hdrlen = ieee80211_hdrlen(fc);
+
+               if (rx->skb->len < 8 + hdrlen)
+                       return RX_DROP_UNUSABLE; /* TODO: count this? */
+
+               /*
+                * no need to call ieee80211_wep_get_keyidx,
+                * it verifies a bunch of things we've done already
+                */
+               skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+               keyidx = keyid >> 6;
+
+               /* check per-station GTK first, if multicast packet */
+               if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
+                       rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
+
+               /* if not found, try default key */
+               if (!rx->key) {
+                       rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+                       /*
+                        * RSNA-protected unicast frames should always be
+                        * sent with pairwise or station-to-station keys,
+                        * but for WEP we allow using a key index as well.
+                        */
+                       if (rx->key &&
+                           rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+                           rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
+                           !is_multicast_ether_addr(hdr->addr1))
+                               rx->key = NULL;
+               }
+       }
+
+       if (rx->key) {
+               if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
+                       return RX_DROP_MONITOR;
+
+               rx->key->tx_rx_count++;
+               /* TODO: add threshold stuff again */
+       } else {
+               return RX_DROP_MONITOR;
+       }
+
+       switch (rx->key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               result = ieee80211_crypto_wep_decrypt(rx);
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               result = ieee80211_crypto_tkip_decrypt(rx);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               result = ieee80211_crypto_ccmp_decrypt(rx);
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               result = ieee80211_crypto_aes_cmac_decrypt(rx);
+               break;
+       default:
+               /*
+                * We can reach here only with HW-only algorithms
+                * but why didn't it decrypt the frame?!
+                */
+               return RX_DROP_UNUSABLE;
+       }
+
+       /* the hdr variable is invalid after the decrypt handlers */
+
+       /* either the frame has been decrypted or will be dropped */
+       status->flag |= RX_FLAG_DECRYPTED;
+
+       return result;
+}
+
 static inline struct ieee80211_fragment_entry *
 ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
                         unsigned int frag, unsigned int seq, int rx_queue,
@@ -2896,10 +2939,10 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
                 */
                rx->skb = skb;
 
-               CALL_RXH(ieee80211_rx_h_decrypt)
                CALL_RXH(ieee80211_rx_h_check_more_data)
                CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
                CALL_RXH(ieee80211_rx_h_sta_process)
+               CALL_RXH(ieee80211_rx_h_decrypt)
                CALL_RXH(ieee80211_rx_h_defragment)
                CALL_RXH(ieee80211_rx_h_michael_mic_verify)
                /* must be after MMIC verify so header is counted in MPDU mic */
index 1b122a7..08afe74 100644 (file)
@@ -66,6 +66,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen, srlen;
+       enum nl80211_bss_scan_width scan_width;
        s32 signal = 0;
 
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -73,8 +74,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
                signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-       cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
-                                        mgmt, len, signal, GFP_ATOMIC);
+       scan_width = NL80211_BSS_CHAN_WIDTH_20;
+       if (rx_status->flag & RX_FLAG_5MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_5;
+       if (rx_status->flag & RX_FLAG_10MHZ)
+               scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+       cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
+                                              scan_width, mgmt, len, signal,
+                                              GFP_ATOMIC);
        if (!cbss)
                return NULL;
 
@@ -204,10 +212,29 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
                ieee80211_rx_bss_put(local, bss);
 }
 
+static void
+ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
+                              enum nl80211_bss_scan_width scan_width)
+{
+       memset(chandef, 0, sizeof(*chandef));
+       switch (scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               chandef->width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               chandef->width = NL80211_CHAN_WIDTH_10;
+               break;
+       default:
+               chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
+}
+
 /* return false if no more work */
 static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
        struct cfg80211_scan_request *req = local->scan_req;
+       struct cfg80211_chan_def chandef;
        enum ieee80211_band band;
        int i, ielen, n_chans;
 
@@ -229,11 +256,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        } while (!n_chans);
 
        local->hw_scan_req->n_channels = n_chans;
+       ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
        ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
                                         local->hw_scan_ies_bufsize,
                                         req->ie, req->ie_len, band,
-                                        req->rates[band], 0);
+                                        req->rates[band], &chandef);
        local->hw_scan_req->ie_len = ielen;
        local->hw_scan_req->no_cck = req->no_cck;
 
@@ -280,7 +308,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        rcu_assign_pointer(local->scan_sdata, NULL);
 
        local->scanning = 0;
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
 
        /* Set power back to normal operating levels. */
        ieee80211_hw_config(local, 0);
@@ -615,11 +643,34 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
 {
        int skip;
        struct ieee80211_channel *chan;
+       enum nl80211_bss_scan_width oper_scan_width;
 
        skip = 0;
        chan = local->scan_req->channels[local->scan_channel_idx];
 
-       local->scan_channel = chan;
+       local->scan_chandef.chan = chan;
+       local->scan_chandef.center_freq1 = chan->center_freq;
+       local->scan_chandef.center_freq2 = 0;
+       switch (local->scan_req->scan_width) {
+       case NL80211_BSS_CHAN_WIDTH_5:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_10:
+               local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
+               break;
+       case NL80211_BSS_CHAN_WIDTH_20:
+               /* If scanning on oper channel, use whatever channel-type
+                * is currently in use.
+                */
+               oper_scan_width = cfg80211_chandef_to_scan_width(
+                                       &local->_oper_chandef);
+               if (chan == local->_oper_chandef.chan &&
+                   oper_scan_width == local->scan_req->scan_width)
+                       local->scan_chandef = local->_oper_chandef;
+               else
+                       local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+               break;
+       }
 
        if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
                skip = 1;
@@ -659,7 +710,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
                                         unsigned long *next_delay)
 {
        /* switch back to the operating channel */
-       local->scan_channel = NULL;
+       local->scan_chandef.chan = NULL;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        /* disable PS */
@@ -801,7 +852,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 
 int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
-                               struct ieee80211_channel *chan)
+                               struct ieee80211_channel *chan,
+                               enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_local *local = sdata->local;
        int ret = -EBUSY;
@@ -851,6 +903,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
 
        local->int_scan_req->ssids = &local->scan_ssid;
        local->int_scan_req->n_ssids = 1;
+       local->int_scan_req->scan_width = scan_width;
        memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
        local->int_scan_req->ssids[0].ssid_len = ssid_len;
 
@@ -912,6 +965,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sched_scan_ies sched_scan_ies = {};
+       struct cfg80211_chan_def chandef;
        int ret, i, iebufsz;
 
        iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
@@ -939,10 +993,12 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                        goto out_free;
                }
 
+               ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
                sched_scan_ies.len[i] =
                        ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
                                                 iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, 0);
+                                                i, (u32) -1, &chandef);
        }
 
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
index 4343920..368837f 100644 (file)
@@ -235,7 +235,8 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
 
        /* IEEE80211_RADIOTAP_RATE rate */
        if (info->status.rates[0].idx >= 0 &&
-           !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+           !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+                                            IEEE80211_TX_RC_VHT_MCS)))
                len += 2;
 
        /* IEEE80211_RADIOTAP_TX_FLAGS */
@@ -244,17 +245,23 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
        /* IEEE80211_RADIOTAP_DATA_RETRIES */
        len += 1;
 
-       /* IEEE80211_TX_RC_MCS */
-       if (info->status.rates[0].idx >= 0 &&
-           info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
-               len += 3;
+       /* IEEE80211_RADIOTAP_MCS
+        * IEEE80211_RADIOTAP_VHT */
+       if (info->status.rates[0].idx >= 0) {
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
+                       len += 3;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
+                       len = ALIGN(len, 2) + 12;
+       }
 
        return len;
 }
 
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
-                                            *sband, struct sk_buff *skb,
-                                            int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
+                                struct ieee80211_supported_band *sband,
+                                struct sk_buff *skb, int retry_count,
+                                int rtap_len, int shift)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -279,9 +286,13 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
 
        /* IEEE80211_RADIOTAP_RATE */
        if (info->status.rates[0].idx >= 0 &&
-           !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+           !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+                                            IEEE80211_TX_RC_VHT_MCS))) {
+               u16 rate;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+               rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+               *pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
                /* padding for tx flags */
                pos += 2;
        }
@@ -306,9 +317,12 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
        *pos = retry_count;
        pos++;
 
-       /* IEEE80211_TX_RC_MCS */
-       if (info->status.rates[0].idx >= 0 &&
-           info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+       if (info->status.rates[0].idx < 0)
+               return;
+
+       /* IEEE80211_RADIOTAP_MCS
+        * IEEE80211_RADIOTAP_VHT */
+       if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
                pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
                         IEEE80211_RADIOTAP_MCS_HAVE_GI |
@@ -321,8 +335,48 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
                        pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF;
                pos[2] = info->status.rates[0].idx;
                pos += 3;
-       }
+       } else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
+               u16 known = local->hw.radiotap_vht_details &
+                       (IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+                        IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
+
+               rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+               /* required alignment from rthdr */
+               pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
 
+               /* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
+               put_unaligned_le16(known, pos);
+               pos += 2;
+
+               /* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
+                       *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+               pos++;
+
+               /* u8 bandwidth */
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+                       *pos = 1;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+                       *pos = 4;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+                       *pos = 11;
+               else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */
+                       *pos = 0;
+               pos++;
+
+               /* u8 mcs_nss[4] */
+               *pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) |
+                       ieee80211_rate_get_vht_nss(&info->status.rates[0]);
+               pos += 4;
+
+               /* u8 coding */
+               pos++;
+               /* u8 group_id */
+               pos++;
+               /* u16 partial_aid */
+               pos += 2;
+       }
 }
 
 static void ieee80211_report_used_skb(struct ieee80211_local *local,
@@ -424,6 +478,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        bool acked;
        struct ieee80211_bar *bar;
        int rtap_len;
+       int shift = 0;
 
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -458,6 +513,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
                        continue;
 
+               shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
                if (info->flags & IEEE80211_TX_STATUS_EOSP)
                        clear_sta_flag(sta, WLAN_STA_SP);
 
@@ -557,7 +614,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        rcu_read_unlock();
 
-       ieee80211_led_tx(local, 0);
+       ieee80211_led_tx(local);
 
        /* SNMP counters
         * Fragments are passed to low-level drivers as separate skbs, so these
@@ -624,7 +681,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                dev_kfree_skb(skb);
                return;
        }
-       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+       ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
+                                        rtap_len, shift);
 
        /* XXX: is this sufficient for BPF? */
        skb_set_mac_header(skb, 0);
index c215faf..1aba645 100644 (file)
@@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected,
        )
 );
 
+TRACE_EVENT(drv_channel_switch_beacon,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct cfg80211_chan_def *chandef),
+
+       TP_ARGS(local, sdata, chandef),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               CHANDEF_ENTRY
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               CHANDEF_ASSIGN(chandef);
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
+       )
+);
+
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
index 4105d0c..098ae85 100644 (file)
@@ -40,12 +40,22 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                                 struct sk_buff *skb, int group_addr,
                                 int next_frag_len)
 {
-       int rate, mrate, erp, dur, i;
+       int rate, mrate, erp, dur, i, shift = 0;
        struct ieee80211_rate *txrate;
        struct ieee80211_local *local = tx->local;
        struct ieee80211_supported_band *sband;
        struct ieee80211_hdr *hdr;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       u32 rate_flags = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
+       if (chanctx_conf) {
+               shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+               rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+       }
+       rcu_read_unlock();
 
        /* assume HW handles this */
        if (tx->rate.flags & IEEE80211_TX_RC_MCS)
@@ -122,8 +132,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                if (r->bitrate > txrate->bitrate)
                        break;
 
+               if ((rate_flags & r->flags) != rate_flags)
+                       continue;
+
                if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
-                       rate = r->bitrate;
+                       rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 
                switch (sband->band) {
                case IEEE80211_BAND_2GHZ: {
@@ -150,7 +163,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
        if (rate == -1) {
                /* No matching basic rate found; use highest suitable mandatory
                 * PHY rate */
-               rate = mrate;
+               rate = DIV_ROUND_UP(mrate, 1 << shift);
        }
 
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
@@ -162,7 +175,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
                 * to closest integer */
                dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
 
        if (next_frag_len) {
                /* Frame is fragmented: duration increases with time needed to
@@ -171,7 +185,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
                /* next fragment */
                dur += ieee80211_frame_duration(sband->band, next_frag_len,
                                txrate->bitrate, erp,
-                               tx->sdata->vif.bss_conf.use_short_preamble);
+                               tx->sdata->vif.bss_conf.use_short_preamble,
+                               shift);
        }
 
        return cpu_to_le16(dur);
@@ -524,9 +539,11 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
 
-       if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol &&
-                    tx->sdata->control_port_no_encrypt))
-               info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) {
+               if (tx->sdata->control_port_no_encrypt)
+                       info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+               info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+       }
 
        return TX_CONTINUE;
 }
@@ -1257,6 +1274,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MONITOR:
+               if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+                       vif = &sdata->vif;
+                       break;
+               }
                sdata = rcu_dereference(local->monitor_sdata);
                if (sdata) {
                        vif = &sdata->vif;
@@ -1281,7 +1302,6 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
                                    txpending);
 
        ieee80211_tpt_led_trig_tx(local, fc, led_len);
-       ieee80211_led_tx(local, 1);
 
        WARN_ON_ONCE(!skb_queue_empty(skbs));
 
@@ -2320,6 +2340,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
+                                struct beacon_data *beacon)
+{
+       struct probe_resp *resp;
+       int counter_offset_beacon = sdata->csa_counter_offset_beacon;
+       int counter_offset_presp = sdata->csa_counter_offset_presp;
+
+       /* warn if the driver did not check for/react to csa completeness */
+       if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
+               return;
+
+       ((u8 *)beacon->tail)[counter_offset_beacon]--;
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP &&
+           counter_offset_presp) {
+               rcu_read_lock();
+               resp = rcu_dereference(sdata->u.ap.probe_resp);
+
+               /* if nl80211 accepted the offset, this should not happen. */
+               if (WARN_ON(!resp)) {
+                       rcu_read_unlock();
+                       return;
+               }
+               resp->data[counter_offset_presp]--;
+               rcu_read_unlock();
+       }
+}
+
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct beacon_data *beacon = NULL;
+       u8 *beacon_data;
+       size_t beacon_data_len;
+       int counter_beacon = sdata->csa_counter_offset_beacon;
+       int ret = false;
+
+       if (!ieee80211_sdata_running(sdata))
+               return false;
+
+       rcu_read_lock();
+       if (vif->type == NL80211_IFTYPE_AP) {
+               struct ieee80211_if_ap *ap = &sdata->u.ap;
+
+               beacon = rcu_dereference(ap->beacon);
+               if (WARN_ON(!beacon || !beacon->tail))
+                       goto out;
+               beacon_data = beacon->tail;
+               beacon_data_len = beacon->tail_len;
+       } else {
+               WARN_ON(1);
+               goto out;
+       }
+
+       if (WARN_ON(counter_beacon > beacon_data_len))
+               goto out;
+
+       if (beacon_data[counter_beacon] == 0)
+               ret = true;
+ out:
+       rcu_read_unlock();
+
+       return ret;
+}
+EXPORT_SYMBOL(ieee80211_csa_is_complete);
+
 struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
                                         u16 *tim_offset, u16 *tim_length)
@@ -2350,6 +2445,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
                if (beacon) {
+                       if (sdata->vif.csa_active)
+                               ieee80211_update_csa(sdata, beacon);
+
                        /*
                         * headroom, head length,
                         * tail length and maximum TIM length
index 2265445..e1b34a1 100644 (file)
@@ -107,7 +107,8 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 }
 
 int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
-                            int rate, int erp, int short_preamble)
+                            int rate, int erp, int short_preamble,
+                            int shift)
 {
        int dur;
 
@@ -118,6 +119,9 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
         *
         * rate is in 100 kbps, so divident is multiplied by 10 in the
         * DIV_ROUND_UP() operations.
+        *
+        * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
+        * is assumed to be 0 otherwise.
         */
 
        if (band == IEEE80211_BAND_5GHZ || erp) {
@@ -130,13 +134,23 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
                 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
                 *
                 * T_SYM = 4 usec
-                * 802.11a - 17.5.2: aSIFSTime = 16 usec
+                * 802.11a - 18.5.2: aSIFSTime = 16 usec
                 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
                 *      signal ext = 6 usec
                 */
                dur = 16; /* SIFS + signal ext */
-               dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
-               dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+               dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
+               dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
+
+               /* IEEE 802.11-2012 18.3.2.4: all values above are:
+                *  * times 4 for 5 MHz
+                *  * times 2 for 10 MHz
+                */
+               dur *= 1 << shift;
+
+               /* rates should already consider the channel bandwidth,
+                * don't apply divisor again.
+                */
                dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
                                        4 * rate); /* T_SYM x N_SYM */
        } else {
@@ -168,7 +182,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
 {
        struct ieee80211_sub_if_data *sdata;
        u16 dur;
-       int erp;
+       int erp, shift = 0;
        bool short_preamble = false;
 
        erp = 0;
@@ -177,10 +191,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
        dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
-                                      short_preamble);
+                                      short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -194,7 +209,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -210,17 +225,20 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* CTS duration */
-       dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, 10, bitrate,
+                                      erp, short_preamble, shift);
        /* Data frame duration */
-       dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                       erp, short_preamble, shift);
        /* ACK duration */
-       dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                       erp, short_preamble);
+       dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                       erp, short_preamble, shift);
 
        return cpu_to_le16(dur);
 }
@@ -235,7 +253,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
        struct ieee80211_rate *rate;
        struct ieee80211_sub_if_data *sdata;
        bool short_preamble;
-       int erp;
+       int erp, shift = 0, bitrate;
        u16 dur;
        struct ieee80211_supported_band *sband;
 
@@ -250,15 +268,18 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
                short_preamble = sdata->vif.bss_conf.use_short_preamble;
                if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
                        erp = rate->flags & IEEE80211_RATE_ERP_G;
+               shift = ieee80211_vif_get_shift(vif);
        }
 
+       bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
        /* Data frame duration */
-       dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
-                                      erp, short_preamble);
+       dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
+                                      erp, short_preamble, shift);
        if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
                /* ACK duration */
-               dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
-                                               erp, short_preamble);
+               dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+                                               erp, short_preamble, shift);
        }
 
        return cpu_to_le16(dur);
@@ -1052,32 +1073,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        }
 }
 
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                 const size_t supp_rates_len,
-                                 const u8 *supp_rates)
-{
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       int i, have_higher_than_11mbit = 0;
-
-       /* cf. IEEE 802.11 9.2.12 */
-       for (i = 0; i < supp_rates_len; i++)
-               if ((supp_rates[i] & 0x7f) * 5 > 110)
-                       have_higher_than_11mbit = 1;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
-       if (chanctx_conf &&
-           chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
-           have_higher_than_11mbit)
-               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-       else
-               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-       rcu_read_unlock();
-
-       ieee80211_set_wmm_default(sdata, true);
-}
-
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *da,
@@ -1162,7 +1157,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             size_t buffer_len, const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
-                            u8 channel)
+                            struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
@@ -1171,16 +1166,26 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
        u8 rates[32];
        int num_rates;
        int ext_rates_len;
+       int shift;
+       u32 rate_flags;
 
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
 
+       rate_flags = ieee80211_chandef_rate_flags(chandef);
+       shift = ieee80211_chandef_get_shift(chandef);
+
        num_rates = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if ((BIT(i) & rate_mask) == 0)
                        continue; /* skip rate */
-               rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
+               rates[num_rates++] =
+                       (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                         (1 << shift) * 5);
        }
 
        supp_rates_len = min_t(int, num_rates, 8);
@@ -1220,12 +1225,13 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                pos += ext_rates_len;
        }
 
-       if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+       if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
                if (end - pos < 3)
                        goto out_err;
                *pos++ = WLAN_EID_DS_PARAMS;
                *pos++ = 1;
-               *pos++ = channel;
+               *pos++ = ieee80211_frequency_to_channel(
+                               chandef->chan->center_freq);
        }
 
        /* insert custom IEs that go before HT */
@@ -1290,9 +1296,9 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          bool directed)
 {
        struct ieee80211_local *local = sdata->local;
+       struct cfg80211_chan_def chandef;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 chan_no;
        int ies_len;
 
        /*
@@ -1300,10 +1306,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
         * in order to maximize the chance that we get a response.  Some
         * badly-behaved APs don't respond when this parameter is included.
         */
+       chandef.width = sdata->vif.bss_conf.chandef.width;
        if (directed)
-               chan_no = 0;
+               chandef.chan = NULL;
        else
-               chan_no = ieee80211_frequency_to_channel(chan->center_freq);
+               chandef.chan = chan;
 
        skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
                                     ssid, ssid_len, 100 + ie_len);
@@ -1313,7 +1320,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
                                           skb_tailroom(skb),
                                           ie, ie_len, chan->band,
-                                          ratemask, chan_no);
+                                          ratemask, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {
@@ -1347,16 +1354,19 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        }
 }
 
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates)
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *bitrates;
        size_t num_rates;
-       u32 supp_rates;
-       int i, j;
-       sband = local->hw.wiphy->bands[band];
+       u32 supp_rates, rate_flags;
+       int i, j, shift;
+       sband = sdata->local->hw.wiphy->bands[band];
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        if (WARN_ON(!sband))
                return 1;
@@ -1381,7 +1391,15 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
                        continue;
 
                for (j = 0; j < num_rates; j++) {
-                       if (bitrates[j].bitrate == own_rate) {
+                       int brate;
+                       if ((rate_flags & sband->bitrates[j].flags)
+                           != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+                                            1 << shift);
+
+                       if (brate == own_rate) {
                                supp_rates |= BIT(j);
                                if (basic_rates && is_basic)
                                        *basic_rates |= BIT(j);
@@ -1435,8 +1453,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                local->resuming = true;
 
        if (local->wowlan) {
-               local->wowlan = false;
                res = drv_resume(local);
+               local->wowlan = false;
                if (res < 0) {
                        local->resuming = false;
                        return res;
@@ -2004,18 +2022,56 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
        cfg80211_chandef_create(chandef, control_chan, channel_type);
 }
 
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+                            const struct ieee80211_supported_band *sband,
+                            const u8 *srates, int srates_len, u32 *rates)
+{
+       u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
+       int shift = ieee80211_chandef_get_shift(chandef);
+       struct ieee80211_rate *br;
+       int brate, rate, i, j, count = 0;
+
+       *rates = 0;
+
+       for (i = 0; i < srates_len; i++) {
+               rate = srates[i] & 0x7f;
+
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       br = &sband->bitrates[j];
+                       if ((rate_flags & br->flags) != rate_flags)
+                               continue;
+
+                       brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+                       if (brate == rate) {
+                               *rates |= BIT(j);
+                               count++;
+                               break;
+                       }
+               }
+       }
+       return count;
+}
+
 int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb, bool need_basic,
                            enum ieee80211_band band)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, shift;
        u8 i, rates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
 
+       shift = ieee80211_vif_get_shift(&sdata->vif);
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
        sband = local->hw.wiphy->bands[band];
-       rates = sband->n_bitrates;
+       rates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               rates++;
+       }
        if (rates > 8)
                rates = 8;
 
@@ -2027,10 +2083,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = rates;
        for (i = 0; i < rates; i++) {
                u8 basic = 0;
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+
                if (need_basic && basic_rates & BIT(i))
                        basic = 0x80;
                rate = sband->bitrates[i].bitrate;
-               *pos++ = basic | (u8) (rate / 5);
+               rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                   5 * (1 << shift));
+               *pos++ = basic | (u8) rate;
        }
 
        return 0;
@@ -2042,12 +2103,22 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
-       int rate;
+       int rate, skip, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+       u32 rate_flags;
+
+       rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+       shift = ieee80211_vif_get_shift(&sdata->vif);
 
        sband = local->hw.wiphy->bands[band];
-       exrates = sband->n_bitrates;
+       exrates = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+                       continue;
+               exrates++;
+       }
+
        if (exrates > 8)
                exrates -= 8;
        else
@@ -2060,12 +2131,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
+               skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
+                       if ((rate_flags & sband->bitrates[i].flags)
+                           != rate_flags)
+                               continue;
+                       if (skip++ < 8)
+                               continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
-                       rate = sband->bitrates[i].bitrate;
-                       *pos++ = basic | (u8) (rate / 5);
+                       rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+                                           5 * (1 << shift));
+                       *pos++ = basic | (u8) rate;
                }
        }
        return 0;
@@ -2149,9 +2227,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
                        ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
        } else {
                struct ieee80211_supported_band *sband;
+               int shift = 0;
+               int bitrate;
+
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
 
                sband = local->hw.wiphy->bands[status->band];
-               ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+               bitrate = sband->bitrates[status->rate_idx].bitrate;
+               ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
        }
 
        rate = cfg80211_calculate_bitrate(&ri);
index 1d074dd..e92923c 100644 (file)
@@ -77,11 +77,19 @@ error:
        return rc;
 }
 
-int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
+/**
+ * nfc_fw_download_done - inform that a firmware download was completed
+ *
+ * @dev: The nfc device to which firmware was downloaded
+ * @firmware_name: The firmware filename
+ * @result: The positive value of a standard errno value
+ */
+int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+                        u32 result)
 {
        dev->fw_download_in_progress = false;
 
-       return nfc_genl_fw_download_done(dev, firmware_name);
+       return nfc_genl_fw_download_done(dev, firmware_name, result);
 }
 EXPORT_SYMBOL(nfc_fw_download_done);
 
@@ -129,7 +137,7 @@ int nfc_dev_up(struct nfc_dev *dev)
        /* We have to enable the device before discovering SEs */
        if (dev->ops->discover_se) {
                rc = dev->ops->discover_se(dev);
-               if (!rc)
+               if (rc)
                        pr_warn("SE discovery failed\n");
        }
 
@@ -575,12 +583,14 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       if (se->type == NFC_SE_ENABLED) {
+       if (se->state == NFC_SE_ENABLED) {
                rc = -EALREADY;
                goto error;
        }
 
        rc = dev->ops->enable_se(dev, se_idx);
+       if (rc >= 0)
+               se->state = NFC_SE_ENABLED;
 
 error:
        device_unlock(&dev->dev);
@@ -618,12 +628,14 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
                goto error;
        }
 
-       if (se->type == NFC_SE_DISABLED) {
+       if (se->state == NFC_SE_DISABLED) {
                rc = -EALREADY;
                goto error;
        }
 
        rc = dev->ops->disable_se(dev, se_idx);
+       if (rc >= 0)
+               se->state = NFC_SE_DISABLED;
 
 error:
        device_unlock(&dev->dev);
index fe66908..d07ca4c 100644 (file)
@@ -717,7 +717,7 @@ static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
        if (hdev->ops->disable_se)
-               return hdev->ops->enable_se(hdev, se_idx);
+               return hdev->ops->disable_se(hdev, se_idx);
 
        return 0;
 }
index f16fd59..68063b2 100644 (file)
@@ -1114,7 +1114,8 @@ static int nfc_genl_fw_download(struct sk_buff *skb, struct genl_info *info)
        return rc;
 }
 
-int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
+int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+                             u32 result)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -1129,6 +1130,7 @@ int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
                goto free_msg;
 
        if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+           nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
                goto nla_put_failure;
 
@@ -1191,6 +1193,91 @@ static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
        return rc;
 }
 
+static int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev,
+                               u32 portid, u32 seq,
+                               struct netlink_callback *cb,
+                               int flags)
+{
+       void *hdr;
+       struct nfc_se *se, *n;
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+               hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
+                                 NFC_CMD_GET_SE);
+               if (!hdr)
+                       goto nla_put_failure;
+
+               if (cb)
+                       genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+               if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+                   nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) ||
+                   nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
+                       goto nla_put_failure;
+
+               if (genlmsg_end(msg, hdr) < 0)
+                       goto nla_put_failure;
+       }
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_dump_ses(struct sk_buff *skb,
+                                struct netlink_callback *cb)
+{
+       struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+       struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+       bool first_call = false;
+
+       if (!iter) {
+               first_call = true;
+               iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+               if (!iter)
+                       return -ENOMEM;
+               cb->args[0] = (long) iter;
+       }
+
+       mutex_lock(&nfc_devlist_mutex);
+
+       cb->seq = nfc_devlist_generation;
+
+       if (first_call) {
+               nfc_device_iter_init(iter);
+               dev = nfc_device_iter_next(iter);
+       }
+
+       while (dev) {
+               int rc;
+
+               rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid,
+                                         cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
+               if (rc < 0)
+                       break;
+
+               dev = nfc_device_iter_next(iter);
+       }
+
+       mutex_unlock(&nfc_devlist_mutex);
+
+       cb->args[1] = (long) dev;
+
+       return skb->len;
+}
+
+static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
+{
+       struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+
+       nfc_device_iter_exit(iter);
+       kfree(iter);
+
+       return 0;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1265,6 +1352,12 @@ static struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_disable_se,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_GET_SE,
+               .dumpit = nfc_genl_dump_ses,
+               .done = nfc_genl_dump_ses_done,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index 820a785..aaf606f 100644 (file)
@@ -124,9 +124,8 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
 }
 
 int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name);
-int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
-
-int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+                             u32 result);
 
 int nfc_dev_up(struct nfc_dev *dev);
 
index d11ac79..cf5b145 100644 (file)
@@ -30,6 +30,7 @@ struct rfkill_regulator_data {
 static int rfkill_regulator_set_block(void *data, bool blocked)
 {
        struct rfkill_regulator_data *rfkill_data = data;
+       int ret = 0;
 
        pr_debug("%s: blocked: %d\n", __func__, blocked);
 
@@ -40,15 +41,16 @@ static int rfkill_regulator_set_block(void *data, bool blocked)
                }
        } else {
                if (!rfkill_data->reg_enabled) {
-                       regulator_enable(rfkill_data->vcc);
-                       rfkill_data->reg_enabled = true;
+                       ret = regulator_enable(rfkill_data->vcc);
+                       if (!ret)
+                               rfkill_data->reg_enabled = true;
                }
        }
 
        pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__,
                regulator_is_enabled(rfkill_data->vcc));
 
-       return 0;
+       return ret;
 }
 
 static struct rfkill_ops rfkill_regulator_ops = {
index a8c29fa..6715396 100644 (file)
@@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy)
                return -EINVAL;
 #endif
 
+       if (WARN_ON(wiphy->coalesce &&
+                   (!wiphy->coalesce->n_rules ||
+                    !wiphy->coalesce->n_patterns) &&
+                   (!wiphy->coalesce->pattern_min_len ||
+                    wiphy->coalesce->pattern_min_len >
+                       wiphy->coalesce->pattern_max_len)))
+               return -EINVAL;
+
        if (WARN_ON(wiphy->ap_sme_capa &&
                    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
                return -EINVAL;
@@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy)
                rdev_set_wakeup(rdev, false);
 #endif
        cfg80211_rdev_free_wowlan(rdev);
+       cfg80211_rdev_free_coalesce(rdev);
 }
 EXPORT_SYMBOL(wiphy_unregister);
 
index a6b45bf..9ad43c6 100644 (file)
@@ -79,6 +79,8 @@ struct cfg80211_registered_device {
        /* netlink port which started critical protocol (0 means not started) */
        u32 crit_proto_nlportid;
 
+       struct cfg80211_coalesce *coalesce;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
index 30c4920..0553fd4 100644 (file)
@@ -167,9 +167,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
         * basic rates
         */
        if (!setup->basic_rates) {
+               enum nl80211_bss_scan_width scan_width;
                struct ieee80211_supported_band *sband =
                                rdev->wiphy.bands[setup->chandef.chan->band];
-               setup->basic_rates = ieee80211_mandatory_rates(sband);
+               scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+               setup->basic_rates = ieee80211_mandatory_rates(sband,
+                                                              scan_width);
        }
 
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
index 5f6e982..741368c 100644 (file)
@@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
                                  .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+       [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+       [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+       [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -403,6 +408,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
        [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
 };
 
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+       [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+       [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
 /* policy for GTK rekey offload attributes */
 static const struct nla_policy
 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@ -976,7 +989,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                return -ENOBUFS;
 
        if (dev->wiphy.wowlan->n_patterns) {
-               struct nl80211_wowlan_pattern_support pat = {
+               struct nl80211_pattern_support pat = {
                        .max_patterns = dev->wiphy.wowlan->n_patterns,
                        .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
                        .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
@@ -997,6 +1010,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 }
 #endif
 
+static int nl80211_send_coalesce(struct sk_buff *msg,
+                                struct cfg80211_registered_device *dev)
+{
+       struct nl80211_coalesce_rule_support rule;
+
+       if (!dev->wiphy.coalesce)
+               return 0;
+
+       rule.max_rules = dev->wiphy.coalesce->n_rules;
+       rule.max_delay = dev->wiphy.coalesce->max_delay;
+       rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
+       rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
+       rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
+       rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+
+       if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+               return -ENOBUFS;
+
+       return 0;
+}
+
 static int nl80211_send_band_rateinfo(struct sk_buff *msg,
                                      struct ieee80211_supported_band *sband)
 {
@@ -1395,6 +1429,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+                       if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+                               CMD(channel_switch, CHANNEL_SWITCH);
                }
 
 #ifdef CONFIG_NL80211_TESTMODE
@@ -1515,6 +1551,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                            dev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
 
+               state->split_start++;
+               break;
+       case 10:
+               if (nl80211_send_coalesce(msg, dev))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -5580,6 +5622,111 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        return err;
 }
 
+static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_csa_settings params;
+       /* csa_attrs is defined static to avoid waste of stack size - this
+        * function is called under RTNL lock, so this should not be a problem.
+        */
+       static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+       u8 radar_detect_width = 0;
+       int err;
+
+       if (!rdev->ops->channel_switch ||
+           !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+
+       /* may add IBSS support later */
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       memset(&params, 0, sizeof(params));
+
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+           !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+               return -EINVAL;
+
+       /* only important for AP, IBSS and mesh create IEs internally */
+       if (!info->attrs[NL80211_ATTR_CSA_IES])
+               return -EINVAL;
+
+       /* useless if AP is not running */
+       if (!wdev->beacon_interval)
+               return -EINVAL;
+
+       params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+
+       err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
+       if (err)
+               return err;
+
+       err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+                              info->attrs[NL80211_ATTR_CSA_IES],
+                              nl80211_policy);
+       if (err)
+               return err;
+
+       err = nl80211_parse_beacon(csa_attrs, &params.beacon_csa);
+       if (err)
+               return err;
+
+       if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+               return -EINVAL;
+
+       params.counter_offset_beacon =
+               nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+       if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+               return -EINVAL;
+
+       /* sanity check - counters should be the same */
+       if (params.beacon_csa.tail[params.counter_offset_beacon] !=
+           params.count)
+               return -EINVAL;
+
+       if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+               params.counter_offset_presp =
+                       nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+               if (params.counter_offset_presp >=
+                   params.beacon_csa.probe_resp_len)
+                       return -EINVAL;
+
+               if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
+                   params.count)
+                       return -EINVAL;
+       }
+
+       err = nl80211_parse_chandef(rdev, info, &params.chandef);
+       if (err)
+               return err;
+
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+               return -EINVAL;
+
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
+       if (err < 0) {
+               return err;
+       } else if (err) {
+               radar_detect_width = BIT(params.chandef.width);
+               params.radar_required = true;
+       }
+
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          params.chandef.chan,
+                                          CHAN_MODE_SHARED,
+                                          radar_detect_width);
+       if (err)
+               return err;
+
+       if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+               params.block_tx = true;
+
+       return rdev_channel_switch(rdev, dev, &params);
+}
+
 static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                            u32 seq, int flags,
                            struct cfg80211_registered_device *rdev,
@@ -5641,6 +5788,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                goto nla_put_failure;
        if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
            nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+           nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
            nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
@@ -6321,6 +6469,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        switch (ibss.chandef.width) {
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
@@ -6348,6 +6498,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               memcpy(&ibss.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(ibss.ht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&ibss.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(ibss.ht_capa));
+       }
+
        if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
            !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
                        nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@@ -6430,19 +6593,30 @@ static struct genl_multicast_group nl80211_testmode_mcgrp = {
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev =
+               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
        int err;
 
+       if (!rdev->ops->testmode_cmd)
+               return -EOPNOTSUPP;
+
+       if (IS_ERR(wdev)) {
+               err = PTR_ERR(wdev);
+               if (err != -EINVAL)
+                       return err;
+               wdev = NULL;
+       } else if (wdev->wiphy != &rdev->wiphy) {
+               return -EINVAL;
+       }
+
        if (!info->attrs[NL80211_ATTR_TESTDATA])
                return -EINVAL;
 
-       err = -EOPNOTSUPP;
-       if (rdev->ops->testmode_cmd) {
-               rdev->testmode_info = info;
-               err = rdev_testmode_cmd(rdev,
+       rdev->testmode_info = info;
+       err = rdev_testmode_cmd(rdev, wdev,
                                nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
                                nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
-               rdev->testmode_info = NULL;
-       }
+       rdev->testmode_info = NULL;
 
        return err;
 }
@@ -7404,14 +7578,12 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
                               u32 rate, u32 pkts, u32 intvl)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct wireless_dev *wdev;
        struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
 
        if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
                return -EINVAL;
 
-       wdev = dev->ieee80211_ptr;
-
        if (!rdev->ops->set_cqm_txe_config)
                return -EOPNOTSUPP;
 
@@ -7426,13 +7598,15 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
                                s32 threshold, u32 hysteresis)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct wireless_dev *wdev;
        struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
 
        if (threshold > 0)
                return -EINVAL;
 
-       wdev = dev->ieee80211_ptr;
+       /* disabling - hysteresis should also be zero then */
+       if (threshold == 0)
+               hysteresis = 0;
 
        if (!rdev->ops->set_cqm_rssi_config)
                return -EOPNOTSUPP;
@@ -7451,36 +7625,33 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
        int err;
 
        cqm = info->attrs[NL80211_ATTR_CQM];
-       if (!cqm) {
-               err = -EINVAL;
-               goto out;
-       }
+       if (!cqm)
+               return -EINVAL;
 
        err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
                               nl80211_attr_cqm_policy);
        if (err)
-               goto out;
+               return err;
 
        if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
            attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
-               s32 threshold;
-               u32 hysteresis;
-               threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
-               hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
-               err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
-       } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
-                  attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
-                  attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
-               u32 rate, pkts, intvl;
-               rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
-               pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
-               intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
-               err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
-       } else
-               err = -EINVAL;
+               s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+               u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
 
-out:
-       return err;
+               return nl80211_set_cqm_rssi(info, threshold, hysteresis);
+       }
+
+       if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
+           attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
+           attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
+               u32 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
+               u32 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
+               u32 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
+
+               return nl80211_set_cqm_txe(info, rate, pkts, intvl);
+       }
+
+       return -EINVAL;
 }
 
 static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
@@ -7596,12 +7767,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                if (!nl_pat)
                        return -ENOBUFS;
                pat_len = wowlan->patterns[i].pattern_len;
-               if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
-                           DIV_ROUND_UP(pat_len, 8),
+               if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
                            wowlan->patterns[i].mask) ||
-                   nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-                           pat_len, wowlan->patterns[i].pattern) ||
-                   nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
+                   nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                           wowlan->patterns[i].pattern) ||
+                   nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
                                wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
@@ -7942,7 +8112,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                struct nlattr *pat;
                int n_patterns = 0;
                int rem, pat_len, mask_len, pkt_offset;
-               struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+               struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
@@ -7961,26 +8131,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
-                       nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
-                                 nla_data(pat), nla_len(pat), NULL);
+                       nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                                 nla_len(pat), NULL);
                        err = -EINVAL;
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
-                           !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+                       if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                           !pat_tb[NL80211_PKTPAT_PATTERN])
                                goto error;
-                       pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+                       pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
                        mask_len = DIV_ROUND_UP(pat_len, 8);
-                       if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
-                           mask_len)
+                       if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
                                goto error;
                        if (pat_len > wowlan->pattern_max_len ||
                            pat_len < wowlan->pattern_min_len)
                                goto error;
 
-                       if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
+                       if (!pat_tb[NL80211_PKTPAT_OFFSET])
                                pkt_offset = 0;
                        else
                                pkt_offset = nla_get_u32(
-                                       pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
+                                       pat_tb[NL80211_PKTPAT_OFFSET]);
                        if (pkt_offset > wowlan->max_pkt_offset)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
@@ -7994,11 +8163,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        new_triggers.patterns[i].pattern =
                                new_triggers.patterns[i].mask + mask_len;
                        memcpy(new_triggers.patterns[i].mask,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+                              nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
                        new_triggers.patterns[i].pattern_len = pat_len;
                        memcpy(new_triggers.patterns[i].pattern,
-                              nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+                              nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
                }
@@ -8037,6 +8206,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 }
 #endif
 
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+                                      struct cfg80211_registered_device *rdev)
+{
+       struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+       int i, j, pat_len;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!rdev->coalesce->n_rules)
+               return 0;
+
+       nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+       if (!nl_rules)
+               return -ENOBUFS;
+
+       for (i = 0; i < rdev->coalesce->n_rules; i++) {
+               nl_rule = nla_nest_start(msg, i + 1);
+               if (!nl_rule)
+                       return -ENOBUFS;
+
+               rule = &rdev->coalesce->rules[i];
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+                               rule->delay))
+                       return -ENOBUFS;
+
+               if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+                               rule->condition))
+                       return -ENOBUFS;
+
+               nl_pats = nla_nest_start(msg,
+                               NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+               if (!nl_pats)
+                       return -ENOBUFS;
+
+               for (j = 0; j < rule->n_patterns; j++) {
+                       nl_pat = nla_nest_start(msg, j + 1);
+                       if (!nl_pat)
+                               return -ENOBUFS;
+                       pat_len = rule->patterns[j].pattern_len;
+                       if (nla_put(msg, NL80211_PKTPAT_MASK,
+                                   DIV_ROUND_UP(pat_len, 8),
+                                   rule->patterns[j].mask) ||
+                           nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+                                   rule->patterns[j].pattern) ||
+                           nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+                                       rule->patterns[j].pkt_offset))
+                               return -ENOBUFS;
+                       nla_nest_end(msg, nl_pat);
+               }
+               nla_nest_end(msg, nl_pats);
+               nla_nest_end(msg, nl_rule);
+       }
+       nla_nest_end(msg, nl_rules);
+
+       return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct sk_buff *msg;
+       void *hdr;
+
+       if (!rdev->wiphy.coalesce)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_COALESCE);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_coalesce *coalesce = rdev->coalesce;
+       int i, j;
+       struct cfg80211_coalesce_rules *rule;
+
+       if (!coalesce)
+               return;
+
+       for (i = 0; i < coalesce->n_rules; i++) {
+               rule = &coalesce->rules[i];
+               for (j = 0; j < rule->n_patterns; j++)
+                       kfree(rule->patterns[j].mask);
+               kfree(rule->patterns);
+       }
+       kfree(coalesce->rules);
+       kfree(coalesce);
+       rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+                                      struct nlattr *rule,
+                                      struct cfg80211_coalesce_rules *new_rule)
+{
+       int err, i;
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+       int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+       struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+       err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
+                       nla_len(rule), nl80211_coalesce_policy);
+       if (err)
+               return err;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+               new_rule->delay =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+       if (new_rule->delay > coalesce->max_delay)
+               return -EINVAL;
+
+       if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+               new_rule->condition =
+                       nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+       if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+           new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+               return -EINVAL;
+
+       if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+               return -EINVAL;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem)
+               n_patterns++;
+       if (n_patterns > coalesce->n_patterns)
+               return -EINVAL;
+
+       new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+                                    GFP_KERNEL);
+       if (!new_rule->patterns)
+               return -ENOMEM;
+
+       new_rule->n_patterns = n_patterns;
+       i = 0;
+
+       nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+                           rem) {
+               nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+                         nla_len(pat), NULL);
+               if (!pat_tb[NL80211_PKTPAT_MASK] ||
+                   !pat_tb[NL80211_PKTPAT_PATTERN])
+                       return -EINVAL;
+               pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+               mask_len = DIV_ROUND_UP(pat_len, 8);
+               if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+                       return -EINVAL;
+               if (pat_len > coalesce->pattern_max_len ||
+                   pat_len < coalesce->pattern_min_len)
+                       return -EINVAL;
+
+               if (!pat_tb[NL80211_PKTPAT_OFFSET])
+                       pkt_offset = 0;
+               else
+                       pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+               if (pkt_offset > coalesce->max_pkt_offset)
+                       return -EINVAL;
+               new_rule->patterns[i].pkt_offset = pkt_offset;
+
+               new_rule->patterns[i].mask =
+                       kmalloc(mask_len + pat_len, GFP_KERNEL);
+               if (!new_rule->patterns[i].mask)
+                       return -ENOMEM;
+               new_rule->patterns[i].pattern =
+                       new_rule->patterns[i].mask + mask_len;
+               memcpy(new_rule->patterns[i].mask,
+                      nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+               new_rule->patterns[i].pattern_len = pat_len;
+               memcpy(new_rule->patterns[i].pattern,
+                      nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+               i++;
+       }
+
+       return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+       struct cfg80211_coalesce new_coalesce = {};
+       struct cfg80211_coalesce *n_coalesce;
+       int err, rem_rule, n_rules = 0, i, j;
+       struct nlattr *rule;
+       struct cfg80211_coalesce_rules *tmp_rule;
+
+       if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+               cfg80211_rdev_free_coalesce(rdev);
+               rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+               return 0;
+       }
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule)
+               n_rules++;
+       if (n_rules > coalesce->n_rules)
+               return -EINVAL;
+
+       new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+                                    GFP_KERNEL);
+       if (!new_coalesce.rules)
+               return -ENOMEM;
+
+       new_coalesce.n_rules = n_rules;
+       i = 0;
+
+       nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+                           rem_rule) {
+               err = nl80211_parse_coalesce_rule(rdev, rule,
+                                                 &new_coalesce.rules[i]);
+               if (err)
+                       goto error;
+
+               i++;
+       }
+
+       err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+       if (err)
+               goto error;
+
+       n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+       if (!n_coalesce) {
+               err = -ENOMEM;
+               goto error;
+       }
+       cfg80211_rdev_free_coalesce(rdev);
+       rdev->coalesce = n_coalesce;
+
+       return 0;
+error:
+       for (i = 0; i < new_coalesce.n_rules; i++) {
+               tmp_rule = &new_coalesce.rules[i];
+               for (j = 0; j < tmp_rule->n_patterns; j++)
+                       kfree(tmp_rule->patterns[j].mask);
+               kfree(tmp_rule->patterns);
+       }
+       kfree(new_coalesce.rules);
+
+       return err;
+}
+
 static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9043,7 +9470,30 @@ static struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
-       }
+       },
+       {
+               .cmd = NL80211_CMD_GET_COALESCE,
+               .doit = nl80211_get_coalesce,
+               .policy = nl80211_policy,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_SET_COALESCE,
+               .doit = nl80211_set_coalesce,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_CHANNEL_SWITCH,
+               .doit = nl80211_channel_switch,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
index a4073e8..44341bf 100644 (file)
@@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
 
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index 9f15f0a..37ce9fd 100644 (file)
@@ -516,11 +516,12 @@ static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
 
 #ifdef CONFIG_NL80211_TESTMODE
 static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
+                                   struct wireless_dev *wdev,
                                    void *data, int len)
 {
        int ret;
-       trace_rdev_testmode_cmd(&rdev->wiphy);
-       ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len);
+       trace_rdev_testmode_cmd(&rdev->wiphy, wdev);
+       ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len);
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
@@ -923,4 +924,16 @@ static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev,
        trace_rdev_return_void(&rdev->wiphy);
 }
 
+static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
+                                     struct net_device *dev,
+                                     struct cfg80211_csa_settings *params)
+{
+       int ret;
+
+       trace_rdev_channel_switch(&rdev->wiphy, dev, params);
+       ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index ae8c186..ad1e406 100644 (file)
@@ -651,6 +651,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
                        continue;
                if (bss->pub.channel != new->pub.channel)
                        continue;
+               if (bss->pub.scan_width != new->pub.scan_width)
+                       continue;
                if (rcu_access_pointer(bss->pub.beacon_ies))
                        continue;
                ies = rcu_access_pointer(bss->pub.ies);
@@ -870,11 +872,12 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss*
-cfg80211_inform_bss(struct wiphy *wiphy,
-                   struct ieee80211_channel *channel,
-                   const u8 *bssid, u64 tsf, u16 capability,
-                   u16 beacon_interval, const u8 *ie, size_t ielen,
-                   s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+                         struct ieee80211_channel *channel,
+                         enum nl80211_bss_scan_width scan_width,
+                         const u8 *bssid, u64 tsf, u16 capability,
+                         u16 beacon_interval, const u8 *ie, size_t ielen,
+                         s32 signal, gfp_t gfp)
 {
        struct cfg80211_bss_ies *ies;
        struct cfg80211_internal_bss tmp = {}, *res;
@@ -892,6 +895,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
 
        memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = beacon_interval;
        tmp.pub.capability = capability;
@@ -924,14 +928,15 @@ cfg80211_inform_bss(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss);
+EXPORT_SYMBOL(cfg80211_inform_bss_width);
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 struct cfg80211_bss *
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
-                         struct ieee80211_channel *channel,
-                         struct ieee80211_mgmt *mgmt, size_t len,
-                         s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+                               struct ieee80211_channel *channel,
+                               enum nl80211_bss_scan_width scan_width,
+                               struct ieee80211_mgmt *mgmt, size_t len,
+                               s32 signal, gfp_t gfp)
 {
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
@@ -941,7 +946,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-       trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
+       trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+                                             len, signal);
 
        if (WARN_ON(!mgmt))
                return NULL;
@@ -976,6 +982,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        
        memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
+       tmp.pub.scan_width = scan_width;
        tmp.pub.signal = signal;
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@@ -991,7 +998,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
        /* cfg80211_bss_update gives us a referenced result */
        return &res->pub;
 }
-EXPORT_SYMBOL(cfg80211_inform_bss_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
index e1534ba..ba5f0d6 100644 (file)
@@ -1293,15 +1293,17 @@ TRACE_EVENT(rdev_return_int_int,
 
 #ifdef CONFIG_NL80211_TESTMODE
 TRACE_EVENT(rdev_testmode_cmd,
-       TP_PROTO(struct wiphy *wiphy),
-       TP_ARGS(wiphy),
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+       TP_ARGS(wiphy, wdev),
        TP_STRUCT__entry(
                WIPHY_ENTRY
+               WDEV_ENTRY
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
+               WDEV_ASSIGN;
        ),
-       TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+       TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
 TRACE_EVENT(rdev_testmode_dump,
@@ -1841,6 +1843,39 @@ TRACE_EVENT(rdev_crit_proto_stop,
                  WIPHY_PR_ARG, WDEV_PR_ARG)
 );
 
+TRACE_EVENT(rdev_channel_switch,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_csa_settings *params),
+       TP_ARGS(wiphy, netdev, params),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               CHAN_DEF_ENTRY
+               __field(u16, counter_offset_beacon)
+               __field(u16, counter_offset_presp)
+               __field(bool, radar_required)
+               __field(bool, block_tx)
+               __field(u8, count)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               CHAN_DEF_ASSIGN(&params->chandef);
+               __entry->counter_offset_beacon = params->counter_offset_beacon;
+               __entry->counter_offset_presp = params->counter_offset_presp;
+               __entry->radar_required = params->radar_required;
+               __entry->block_tx = params->block_tx;
+               __entry->count = params->count;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+                 ", block_tx: %d, count: %u, radar_required: %d"
+                 ", counter offsets (beacon/presp): %u/%u",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+                 __entry->block_tx, __entry->count, __entry->radar_required,
+                 __entry->counter_offset_beacon,
+                 __entry->counter_offset_presp)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
@@ -2391,26 +2426,30 @@ TRACE_EVENT(cfg80211_get_bss,
                  __entry->capa_mask, __entry->capa_val)
 );
 
-TRACE_EVENT(cfg80211_inform_bss_frame,
+TRACE_EVENT(cfg80211_inform_bss_width_frame,
        TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+                enum nl80211_bss_scan_width scan_width,
                 struct ieee80211_mgmt *mgmt, size_t len,
                 s32 signal),
-       TP_ARGS(wiphy, channel, mgmt, len, signal),
+       TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_ENTRY
+               __field(enum nl80211_bss_scan_width, scan_width)
                __dynamic_array(u8, mgmt, len)
                __field(s32, signal)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                CHAN_ASSIGN(channel);
+               __entry->scan_width = scan_width;
                if (mgmt)
                        memcpy(__get_dynamic_array(mgmt), mgmt, len);
                __entry->signal = signal;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
-                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+       TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+                 WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+                 __entry->signal)
 );
 
 DECLARE_EVENT_CLASS(cfg80211_bss_evt,
index 74458b7..ce090c1 100644 (file)
@@ -33,7 +33,8 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
 }
 EXPORT_SYMBOL(ieee80211_get_response_rate);
 
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+                             enum nl80211_bss_scan_width scan_width)
 {
        struct ieee80211_rate *bitrates;
        u32 mandatory_rates = 0;
@@ -43,10 +44,15 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
        if (WARN_ON(!sband))
                return 1;
 
-       if (sband->band == IEEE80211_BAND_2GHZ)
-               mandatory_flag = IEEE80211_RATE_MANDATORY_B;
-       else
+       if (sband->band == IEEE80211_BAND_2GHZ) {
+               if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+                   scan_width == NL80211_BSS_CHAN_WIDTH_10)
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_G;
+               else
+                       mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+       } else {
                mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+       }
 
        bitrates = sband->bitrates;
        for (i = 0; i < sband->n_bitrates; i++)