Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 21 Jun 2013 19:42:30 +0000 (15:42 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 21 Jun 2013 19:42:30 +0000 (15:42 -0400)
Conflicts:
net/wireless/nl80211.c

210 files changed:
Documentation/DocBook/80211.tmpl
drivers/bcma/Kconfig
drivers/bcma/core.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btusb.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hif.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_rx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_tx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hw.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/rx_desc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/targaddrs.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ani.h
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
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_cdc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Makefile
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/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/tx.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/tx.c
drivers/net/wireless/mwifiex/11h.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/sysfs.c [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/sysfs.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/tx.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/microread/microread.c
drivers/nfc/nfcsim.c [new file with mode: 0644]
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/pn544.c
drivers/ssb/driver_chipcommon_sflash.c
drivers/ssb/main.c
drivers/ssb/ssb_private.h
include/linux/ieee80211.h
include/linux/ssb/ssb_driver_mips.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/mac80211/util.c
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/llcp.h
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/llcp_sock.c
net/nfc/nci/Kconfig
net/nfc/nci/Makefile
net/nfc/nci/core.c
net/nfc/nci/data.c
net/nfc/nci/spi.c [new file with mode: 0644]
net/nfc/netlink.c
net/nfc/nfc.h
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/wext-sme.c

index ebe8969..49267ea 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_ibss_params
 !Finclude/net/cfg80211.h cfg80211_connect_params
 !Finclude/net/cfg80211.h cfg80211_pmksa
-!Finclude/net/cfg80211.h cfg80211_send_rx_auth
-!Finclude/net/cfg80211.h cfg80211_send_auth_timeout
-!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
-!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
-!Finclude/net/cfg80211.h cfg80211_send_deauth
-!Finclude/net/cfg80211.h cfg80211_send_disassoc
+!Finclude/net/cfg80211.h cfg80211_rx_mlme_mgmt
+!Finclude/net/cfg80211.h cfg80211_auth_timeout
+!Finclude/net/cfg80211.h cfg80211_rx_assoc_resp
+!Finclude/net/cfg80211.h cfg80211_assoc_timeout
+!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
 !Finclude/net/cfg80211.h cfg80211_ibss_joined
 !Finclude/net/cfg80211.h cfg80211_connect_result
 !Finclude/net/cfg80211.h cfg80211_roamed
index 8b4221c..380a200 100644 (file)
@@ -26,6 +26,7 @@ config BCMA_HOST_PCI_POSSIBLE
 config BCMA_HOST_PCI
        bool "Support for BCMA on PCI-host bus"
        depends on BCMA_HOST_PCI_POSSIBLE
+       default y
 
 config BCMA_DRIVER_PCI_HOSTMODE
        bool "Driver for PCI core working in hostmode"
index 17b26ce..37a5ffe 100644 (file)
@@ -9,6 +9,25 @@
 #include <linux/export.h>
 #include <linux/bcma/bcma.h>
 
+static bool bcma_core_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+                                u32 value, int timeout)
+{
+       unsigned long deadline = jiffies + timeout;
+       u32 val;
+
+       do {
+               val = bcma_aread32(core, reg);
+               if ((val & mask) == value)
+                       return true;
+               cpu_relax();
+               udelay(10);
+       } while (!time_after_eq(jiffies, deadline));
+
+       bcma_warn(core->bus, "Timeout waiting for register 0x%04X!\n", reg);
+
+       return false;
+}
+
 bool bcma_core_is_enabled(struct bcma_device *core)
 {
        if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC))
@@ -25,13 +44,15 @@ void bcma_core_disable(struct bcma_device *core, u32 flags)
        if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
                return;
 
-       bcma_awrite32(core, BCMA_IOCTL, flags);
-       bcma_aread32(core, BCMA_IOCTL);
-       udelay(10);
+       bcma_core_wait_value(core, BCMA_RESET_ST, ~0, 0, 300);
 
        bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
        bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
+
+       bcma_awrite32(core, BCMA_IOCTL, flags);
+       bcma_aread32(core, BCMA_IOCTL);
+       udelay(10);
 }
 EXPORT_SYMBOL_GPL(bcma_core_disable);
 
@@ -43,6 +64,7 @@ int bcma_core_enable(struct bcma_device *core, u32 flags)
        bcma_aread32(core, BCMA_IOCTL);
 
        bcma_awrite32(core, BCMA_RESET_CTL, 0);
+       bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
 
        bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags));
index e6ed4fe..4d07cce 100644 (file)
@@ -30,7 +30,7 @@ struct bcma_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -41,7 +41,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -59,7 +59,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -89,7 +89,7 @@ int bcma_sflash_init(struct bcma_drv_cc *cc)
 {
        struct bcma_bus *bus = cc->core->bus;
        struct bcma_sflash *sflash = &cc->sflash;
-       struct bcma_sflash_tbl_e *e;
+       const struct bcma_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
index 13693b7..75c2626 100644 (file)
@@ -554,6 +554,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
        skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
        if (skb == NULL) {
                BT_ERR("No free skb");
+               ret = -ENOMEM;
                goto exit;
        }
 
index 7a7e5f8..81f1275 100644 (file)
@@ -57,6 +57,9 @@ static struct usb_device_id btusb_table[] = {
        /* Apple-specific (Broadcom) devices */
        { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
 
+       /* MediaTek MT76x0E */
+       { USB_DEVICE(0x0e8d, 0x763f) },
+
        /* Broadcom SoftSailing reporting vendor specific */
        { USB_DEVICE(0x0a5c, 0x21e1) },
 
index 2c02b4e..1abf1d4 100644 (file)
@@ -31,5 +31,6 @@ source "drivers/net/wireless/ath/carl9170/Kconfig"
 source "drivers/net/wireless/ath/ath6kl/Kconfig"
 source "drivers/net/wireless/ath/ar5523/Kconfig"
 source "drivers/net/wireless/ath/wil6210/Kconfig"
+source "drivers/net/wireless/ath/ath10k/Kconfig"
 
 endif
index 97b964d..fb05cfd 100644 (file)
@@ -4,6 +4,7 @@ obj-$(CONFIG_CARL9170)          += carl9170/
 obj-$(CONFIG_ATH6KL)           += ath6kl/
 obj-$(CONFIG_AR5523)           += ar5523/
 obj-$(CONFIG_WIL6210)          += wil6210/
+obj-$(CONFIG_ATH10K)           += ath10k/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
index 4521342..daeafef 100644 (file)
@@ -239,13 +239,12 @@ enum ATH_DEBUG {
        ATH_DBG_CONFIG          = 0x00000200,
        ATH_DBG_FATAL           = 0x00000400,
        ATH_DBG_PS              = 0x00000800,
-       ATH_DBG_HWTIMER         = 0x00001000,
-       ATH_DBG_BTCOEX          = 0x00002000,
-       ATH_DBG_WMI             = 0x00004000,
-       ATH_DBG_BSTUCK          = 0x00008000,
-       ATH_DBG_MCI             = 0x00010000,
-       ATH_DBG_DFS             = 0x00020000,
-       ATH_DBG_WOW             = 0x00040000,
+       ATH_DBG_BTCOEX          = 0x00001000,
+       ATH_DBG_WMI             = 0x00002000,
+       ATH_DBG_BSTUCK          = 0x00004000,
+       ATH_DBG_MCI             = 0x00008000,
+       ATH_DBG_DFS             = 0x00010000,
+       ATH_DBG_WOW             = 0x00020000,
        ATH_DBG_ANY             = 0xffffffff
 };
 
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
new file mode 100644 (file)
index 0000000..cde58fe
--- /dev/null
@@ -0,0 +1,39 @@
+config ATH10K
+        tristate "Atheros 802.11ac wireless cards support"
+        depends on MAC80211
+       select ATH_COMMON
+        ---help---
+          This module adds support for wireless adapters based on
+          Atheros IEEE 802.11ac family of chipsets.
+
+          If you choose to build a module, it'll be called ath10k.
+
+config ATH10K_PCI
+       tristate "Atheros ath10k PCI support"
+       depends on ATH10K && PCI
+       ---help---
+         This module adds support for PCIE bus
+
+config ATH10K_DEBUG
+       bool "Atheros ath10k debugging"
+       depends on ATH10K
+       ---help---
+         Enables debug support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_DEBUGFS
+       bool "Atheros ath10k debugfs support"
+       depends on ATH10K
+       ---help---
+         Enabled debugfs support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_TRACING
+       bool "Atheros ath10k tracing support"
+       depends on ATH10K
+       depends on EVENT_TRACING
+       ---help---
+         Select this to ath10k use tracing infrastructure.
+
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
new file mode 100644 (file)
index 0000000..a4179f4
--- /dev/null
@@ -0,0 +1,20 @@
+obj-$(CONFIG_ATH10K) += ath10k_core.o
+ath10k_core-y += mac.o \
+                debug.o \
+                core.o \
+                htc.o \
+                htt.o \
+                htt_rx.o \
+                htt_tx.o \
+                txrx.o \
+                wmi.o \
+                bmi.o
+
+ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
+
+obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
+ath10k_pci-y += pci.o \
+               ce.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
new file mode 100644 (file)
index 0000000..1a2ef51
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bmi.h"
+#include "hif.h"
+#include "debug.h"
+#include "htc.h"
+
+int ath10k_bmi_done(struct ath10k *ar)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+               return 0;
+       }
+
+       ar->bmi.done_sent = true;
+       cmd.id = __cpu_to_le32(BMI_DONE);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to write to the device: %d\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
+       return 0;
+}
+
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
+       u32 resplen = sizeof(resp.get_target_info);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("BMI Get Target Info Command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to get target info from device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.get_target_info)) {
+               ath10k_warn("invalid get_target_info response length (%d)\n",
+                           resplen);
+               return -EIO;
+       }
+
+       target_info->version = __le32_to_cpu(resp.get_target_info.version);
+       target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+       return 0;
+}
+
+int ath10k_bmi_read_memory(struct ath10k *ar,
+                          u32 address, void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
+       u32 rxlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
+
+               cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
+               cmd.read_mem.addr = __cpu_to_le32(address);
+               cmd.read_mem.len  = __cpu_to_le32(rxlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
+                                                 &resp, &rxlen);
+               if (ret) {
+                       ath10k_warn("unable to read from the device\n");
+                       return ret;
+               }
+
+               memcpy(buffer, resp.read_mem.payload, rxlen);
+               address += rxlen;
+               buffer  += rxlen;
+               length  -= rxlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_write_memory(struct ath10k *ar,
+                           u32 address, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               /* copy before roundup to avoid reading beyond buffer*/
+               memcpy(cmd.write_mem.payload, buffer, txlen);
+               txlen = roundup(txlen, 4);
+
+               cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
+               cmd.write_mem.addr = __cpu_to_le32(address);
+               cmd.write_mem.len  = __cpu_to_le32(txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               /* fixup roundup() so `length` zeroes out for last chunk */
+               txlen = min(txlen, length);
+
+               address += txlen;
+               buffer  += txlen;
+               length  -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
+       u32 resplen = sizeof(resp.execute);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
+                  __func__, ar, address, *param);
+
+       cmd.id            = __cpu_to_le32(BMI_EXECUTE);
+       cmd.execute.addr  = __cpu_to_le32(address);
+       cmd.execute.param = __cpu_to_le32(*param);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to read from the device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.execute)) {
+               ath10k_warn("invalid execute response length (%d)\n",
+                           resplen);
+               return ret;
+       }
+
+       *param = __le32_to_cpu(resp.execute.result);
+       return 0;
+}
+
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               WARN_ON_ONCE(txlen & 3);
+
+               cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
+               cmd.lz_data.len = __cpu_to_le32(txlen);
+               memcpy(cmd.lz_data.payload, buffer, txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               buffer += txlen;
+               length -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
+       cmd.lz_start.addr = __cpu_to_le32(address);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to Start LZ Stream to the device\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_fast_download(struct ath10k *ar,
+                            u32 address, const void *buffer, u32 length)
+{
+       u8 trailer[4] = {};
+       u32 head_len = rounddown(length, 4);
+       u32 trailer_len = length - head_len;
+       int ret;
+
+       ret = ath10k_bmi_lz_stream_start(ar, address);
+       if (ret)
+               return ret;
+
+       /* copy the last word into a zero padded buffer */
+       if (trailer_len > 0)
+               memcpy(trailer, buffer + head_len, trailer_len);
+
+       ret = ath10k_bmi_lz_data(ar, buffer, head_len);
+       if (ret)
+               return ret;
+
+       if (trailer_len > 0)
+               ret = ath10k_bmi_lz_data(ar, trailer, 4);
+
+       if (ret != 0)
+               return ret;
+
+       /*
+        * Close compressed stream and open a new (fake) one.
+        * This serves mainly to flush Target caches.
+        */
+       ret = ath10k_bmi_lz_stream_start(ar, 0x00);
+
+       return ret;
+}
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
new file mode 100644 (file)
index 0000000..32c56aa
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BMI_H_
+#define _BMI_H_
+
+#include "core.h"
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to QCA988x, to provide
+ * patches to code that is already resident on QCA988x, and generally
+ * to examine and modify state.  The Host has an opportunity to use
+ * BMI only once during bootup.  Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0.   BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using AR8K Counter #4.  As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+/* Maximum data size used for BMI transfers */
+#define BMI_MAX_DATA_SIZE      256
+
+/* len = cmd + addr + length */
+#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \
+                       sizeof(u32) + \
+                       sizeof(u32) + \
+                       sizeof(u32))
+
+/* BMI Commands */
+
+enum bmi_cmd_id {
+       BMI_NO_COMMAND          = 0,
+       BMI_DONE                = 1,
+       BMI_READ_MEMORY         = 2,
+       BMI_WRITE_MEMORY        = 3,
+       BMI_EXECUTE             = 4,
+       BMI_SET_APP_START       = 5,
+       BMI_READ_SOC_REGISTER   = 6,
+       BMI_READ_SOC_WORD       = 6,
+       BMI_WRITE_SOC_REGISTER  = 7,
+       BMI_WRITE_SOC_WORD      = 7,
+       BMI_GET_TARGET_ID       = 8,
+       BMI_GET_TARGET_INFO     = 8,
+       BMI_ROMPATCH_INSTALL    = 9,
+       BMI_ROMPATCH_UNINSTALL  = 10,
+       BMI_ROMPATCH_ACTIVATE   = 11,
+       BMI_ROMPATCH_DEACTIVATE = 12,
+       BMI_LZ_STREAM_START     = 13, /* should be followed by LZ_DATA */
+       BMI_LZ_DATA             = 14,
+       BMI_NVRAM_PROCESS       = 15,
+};
+
+#define BMI_NVRAM_SEG_NAME_SZ 16
+
+struct bmi_cmd {
+       __le32 id; /* enum bmi_cmd_id */
+       union {
+               struct {
+               } done;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+               } read_mem;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+                       u8 payload[0];
+               } write_mem;
+               struct {
+                       __le32 addr;
+                       __le32 param;
+               } execute;
+               struct {
+                       __le32 addr;
+               } set_app_start;
+               struct {
+                       __le32 addr;
+               } read_soc_reg;
+               struct {
+                       __le32 addr;
+                       __le32 value;
+               } write_soc_reg;
+               struct {
+               } get_target_info;
+               struct {
+                       __le32 rom_addr;
+                       __le32 ram_addr; /* or value */
+                       __le32 size;
+                       __le32 activate; /* 0=install, but dont activate */
+               } rompatch_install;
+               struct {
+                       __le32 patch_id;
+               } rompatch_uninstall;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_activate;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_deactivate;
+               struct {
+                       __le32 addr;
+               } lz_start;
+               struct {
+                       __le32 len; /* max BMI_MAX_DATA_SIZE */
+                       u8 payload[0]; /* length of @len */
+               } lz_data;
+               struct {
+                       u8 name[BMI_NVRAM_SEG_NAME_SZ];
+               } nvram_process;
+               u8 payload[BMI_MAX_CMDBUF_SIZE];
+       };
+} __packed;
+
+union bmi_resp {
+       struct {
+               u8 payload[0];
+       } read_mem;
+       struct {
+               __le32 result;
+       } execute;
+       struct {
+               __le32 value;
+       } read_soc_reg;
+       struct {
+               __le32 len;
+               __le32 version;
+               __le32 type;
+       } get_target_info;
+       struct {
+               __le32 patch_id;
+       } rompatch_install;
+       struct {
+               __le32 patch_id;
+       } rompatch_uninstall;
+       struct {
+               /* 0 = nothing executed
+                * otherwise = NVRAM segment return value */
+               __le32 result;
+       } nvram_process;
+       u8 payload[BMI_MAX_CMDBUF_SIZE];
+} __packed;
+
+struct bmi_target_info {
+       u32 version;
+       u32 type;
+};
+
+
+/* in msec */
+#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ)
+
+#define BMI_CE_NUM_TO_TARG 0
+#define BMI_CE_NUM_TO_HOST 1
+
+int ath10k_bmi_done(struct ath10k *ar);
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info);
+int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
+                          void *buffer, u32 length);
+int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
+                           const void *buffer, u32 length);
+
+#define ath10k_bmi_read32(ar, item, val)                               \
+       ({                                                              \
+               int ret;                                                \
+               u32 addr;                                               \
+               __le32 tmp;                                             \
+                                                                       \
+               addr = host_interest_item_address(HI_ITEM(item));       \
+               ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
+               *val = __le32_to_cpu(tmp);                              \
+               ret;                                                    \
+        })
+
+#define ath10k_bmi_write32(ar, item, val)                              \
+       ({                                                              \
+               int ret;                                                \
+               u32 address;                                            \
+               __le32 v = __cpu_to_le32(val);                          \
+                                                                       \
+               address = host_interest_item_address(HI_ITEM(item));    \
+               ret = ath10k_bmi_write_memory(ar, address,              \
+                                             (u8 *)&v, sizeof(v));     \
+               ret;                                                    \
+       })
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
+int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
+                            const void *buffer, u32 length);
+#endif /* _BMI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
new file mode 100644 (file)
index 0000000..61a8ac7
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hif.h"
+#include "pci.h"
+#include "ce.h"
+#include "debug.h"
+
+/*
+ * Support for Copy Engine hardware, which is mainly used for
+ * communication between Host and Target over a PCIe interconnect.
+ */
+
+/*
+ * A single CopyEngine (CE) comprises two "rings":
+ *   a source ring
+ *   a destination ring
+ *
+ * Each ring consists of a number of descriptors which specify
+ * an address, length, and meta-data.
+ *
+ * Typically, one side of the PCIe interconnect (Host or Target)
+ * controls one ring and the other side controls the other ring.
+ * The source side chooses when to initiate a transfer and it
+ * chooses what to send (buffer address, length). The destination
+ * side keeps a supply of "anonymous receive buffers" available and
+ * it handles incoming data as it arrives (when the destination
+ * recieves an interrupt).
+ *
+ * The sender may send a simple buffer (address/length) or it may
+ * send a small list of buffers.  When a small list is sent, hardware
+ * "gathers" these and they end up in a single destination buffer
+ * with a single interrupt.
+ *
+ * There are several "contexts" managed by this layer -- more, it
+ * may seem -- than should be needed. These are provided mainly for
+ * maximum flexibility and especially to facilitate a simpler HIF
+ * implementation. There are per-CopyEngine recv, send, and watermark
+ * contexts. These are supplied by the caller when a recv, send,
+ * or watermark handler is established and they are echoed back to
+ * the caller when the respective callbacks are invoked. There is
+ * also a per-transfer context supplied by the caller when a buffer
+ * (or sendlist) is sent and when a buffer is enqueued for recv.
+ * These per-transfer contexts are echoed back to the caller when
+ * the buffer is sent/received.
+ */
+
+static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
+                                                      u32 ce_ctrl_addr,
+                                                      unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+}
+
+static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr,
+                                                     unsigned int n)
+{
+       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)) {
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+               return;
+       }
+
+       /* workaround for QCA988x_1.0 HW CE */
+       indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
+
+       if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
+               iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
+       } else {
+               unsigned long irq_flags;
+               local_irq_save(irq_flags);
+               iowrite32(1, indicator_addr);
+
+               /*
+                * PCIE write waits for ACK in IPQ8K, there is no
+                * need to read back value.
+                */
+               (void)ioread32(indicator_addr);
+               (void)ioread32(indicator_addr); /* conservative */
+
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+
+               iowrite32(0, indicator_addr);
+               local_irq_restore(irq_flags);
+       }
+}
+
+static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+}
+
+static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32((ar),
+                                          (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr &  ~CE_CTRL1_DMAX_LENGTH_MASK) |
+                          CE_CTRL1_DMAX_LENGTH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    u32 addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
+                                               u32 ce_ctrl_addr,
+                                               unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_HIGH_MASK) |
+                          SRC_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
+                                                 u32 ce_ctrl_addr,
+                                                 unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_LOW_MASK) |
+                          SRC_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_HIGH_MASK) |
+                          DST_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_LOW_MASK) |
+                          DST_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~CE_WATERMARK_MASK);
+}
+
+static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
+                                              u32 ce_ctrl_addr)
+{
+       u32 misc_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + MISC_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
+                          misc_ie_addr | CE_ERROR_MASK);
+}
+
+static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int mask)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+}
+
+
+/*
+ * Guts of ath10k_ce_send, used by both ath10k_ce_send and
+ * ath10k_ce_sendlist_send.
+ * The caller takes responsibility for any needed locking.
+ */
+static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+                                void *per_transfer_context,
+                                u32 buffer,
+                                unsigned int nbytes,
+                                unsigned int transfer_id,
+                                unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_desc *desc, *sdesc;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int write_index = src_ring->write_index;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       u32 desc_flags = 0;
+       int ret = 0;
+
+       if (nbytes > ce_state->src_sz_max)
+               ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
+                           __func__, nbytes, ce_state->src_sz_max);
+
+       ath10k_pci_wake(ar);
+
+       if (unlikely(CE_RING_DELTA(nentries_mask,
+                                  write_index, sw_index - 1) <= 0)) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space,
+                                  write_index);
+       sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index);
+
+       desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA);
+
+       if (flags & CE_SEND_FLAG_GATHER)
+               desc_flags |= CE_DESC_FLAGS_GATHER;
+       if (flags & CE_SEND_FLAG_BYTE_SWAP)
+               desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
+
+       sdesc->addr   = __cpu_to_le32(buffer);
+       sdesc->nbytes = __cpu_to_le16(nbytes);
+       sdesc->flags  = __cpu_to_le16(desc_flags);
+
+       *desc = *sdesc;
+
+       src_ring->per_transfer_context[write_index] = per_transfer_context;
+
+       /* Update Source Ring Write Index */
+       write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+
+       /* WORKAROUND */
+       if (!(flags & CE_SEND_FLAG_GATHER))
+               ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+
+       src_ring->write_index = write_index;
+exit:
+       ath10k_pci_sleep(ar);
+       return ret;
+}
+
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  unsigned int transfer_id,
+                  unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                   buffer, nbytes, transfer_id, flags);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
+                               unsigned int nbytes, u32 flags)
+{
+       unsigned int num_items = sendlist->num_items;
+       struct ce_sendlist_item *item;
+
+       item = &sendlist->item[num_items];
+       item->data = buffer;
+       item->u.nbytes = nbytes;
+       item->flags = flags;
+       sendlist->num_items++;
+}
+
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_context,
+                           struct ce_sendlist *sendlist,
+                           unsigned int transfer_id)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_sendlist_item *item;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int num_items = sendlist->num_items;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int i, delta, ret = -ENOMEM;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
+
+       if (delta >= num_items) {
+               /*
+                * Handle all but the last item uniformly.
+                */
+               for (i = 0; i < num_items - 1; i++) {
+                       item = &sendlist->item[i];
+                       ret = ath10k_ce_send_nolock(ce_state,
+                                                   CE_SENDLIST_ITEM_CTXT,
+                                                   (u32) item->data,
+                                                   item->u.nbytes, transfer_id,
+                                                   item->flags |
+                                                   CE_SEND_FLAG_GATHER);
+                       if (ret)
+                               ath10k_warn("CE send failed for item: %d\n", i);
+               }
+               /*
+                * Provide valid context pointer for final item.
+                */
+               item = &sendlist->item[i];
+               ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                           (u32) item->data, item->u.nbytes,
+                                           transfer_id, item->flags);
+               if (ret)
+                       ath10k_warn("CE send failed for last item: %d\n", i);
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_recv_context,
+                              u32 buffer)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int write_index;
+       unsigned int sw_index;
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       write_index = dest_ring->write_index;
+       sw_index = dest_ring->sw_index;
+
+       ath10k_pci_wake(ar);
+
+       if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
+
+               /* Update destination descriptor */
+               desc->addr    = __cpu_to_le32(buffer);
+               desc->nbytes = 0;
+
+               dest_ring->per_transfer_context[write_index] =
+                                                       per_recv_context;
+
+               /* Update Destination Ring Write Index */
+               write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+               ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
+               dest_ring->write_index = write_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+       ath10k_pci_sleep(ar);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_recv_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp,
+                                               unsigned int *flagsp)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int sw_index = dest_ring->sw_index;
+
+       struct ce_desc *base = dest_ring->base_addr_owner_space;
+       struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+       struct ce_desc sdesc;
+       u16 nbytes;
+
+       /* Copy in one go for performance reasons */
+       sdesc = *desc;
+
+       nbytes = __le16_to_cpu(sdesc.nbytes);
+       if (nbytes == 0) {
+               /*
+                * This closes a relatively unusual race where the Host
+                * sees the updated DRRI before the update to the
+                * corresponding descriptor has completed. We treat this
+                * as a descriptor that is not yet done.
+                */
+               return -EIO;
+       }
+
+       desc->nbytes = 0;
+
+       /* Return data from completed destination descriptor */
+       *bufferp = __le32_to_cpu(sdesc.addr);
+       *nbytesp = nbytes;
+       *transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA);
+
+       if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP)
+               *flagsp = CE_RECV_FLAG_SWAPPED;
+       else
+               *flagsp = 0;
+
+       if (per_transfer_contextp)
+               *per_transfer_contextp =
+                       dest_ring->per_transfer_context[sw_index];
+
+       /* sanity */
+       dest_ring->per_transfer_context[sw_index] = NULL;
+
+       /* Update sw_index */
+       sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+       dest_ring->sw_index = sw_index;
+
+       return 0;
+}
+
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp, flagsp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp)
+{
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       dest_ring = ce_state->dest_ring;
+
+       if (!dest_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = dest_ring->nentries_mask;
+       sw_index = dest_ring->sw_index;
+       write_index = dest_ring->write_index;
+       if (write_index != sw_index) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed destination descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               dest_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               dest_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               dest_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_send_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int read_index;
+       int ret = -EIO;
+
+       if (src_ring->hw_index == sw_index) {
+               /*
+                * The SW completion index has caught up with the cached
+                * version of the HW completion index.
+                * Update the cached HW completion index to see whether
+                * the SW has really caught up to the HW, or if the cached
+                * value of the HW index has become stale.
+                */
+               ath10k_pci_wake(ar);
+               src_ring->hw_index =
+                       ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               ath10k_pci_sleep(ar);
+       }
+       read_index = src_ring->hw_index;
+
+       if ((read_index != sw_index) && (read_index != 0xffffffff)) {
+               struct ce_desc *sbase = src_ring->shadow_base;
+               struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(sdesc->addr);
+               *nbytesp = __le16_to_cpu(sdesc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* NB: Modeled after ath10k_ce_completed_send_next */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       src_ring = ce_state->src_ring;
+
+       if (!src_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = src_ring->nentries_mask;
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       if (write_index != sw_index) {
+               struct ce_desc *base = src_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+               *nbytesp = __le16_to_cpu(desc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(desc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_send_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of interrupt handler for per-engine interrupts on a particular CE.
+ *
+ * Invokes registered callbacks for recv_complete,
+ * send_complete, and watermarks.
+ */
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       void *transfer_context;
+       u32 buf;
+       unsigned int nbytes;
+       unsigned int id;
+       unsigned int flags;
+
+       ath10k_pci_wake(ar);
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       /* Clear the copy-complete interrupts that will be handled here. */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+                                         HOST_IS_COPY_COMPLETE_MASK);
+
+       if (ce_state->recv_cb) {
+               /*
+                * Pop completed recv buffers and call the registered
+                * recv callback for each
+                */
+               while (ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf, &nbytes,
+                                                           &id, &flags) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->recv_cb(ce_state, transfer_context, buf,
+                                         nbytes, id, flags);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       if (ce_state->send_cb) {
+               /*
+                * Pop completed send buffers and call the registered
+                * send callback for each
+                */
+               while (ath10k_ce_completed_send_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf,
+                                                           &nbytes,
+                                                           &id) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->send_cb(ce_state, transfer_context,
+                                         buf, nbytes, id);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       /*
+        * Misc CE interrupts are not being handled, but still need
+        * to be cleared.
+        */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Handler for per-engine interrupts on ALL active CEs.
+ * This is used in cases where the system is sharing a
+ * single interrput for all CEs
+ */
+
+void ath10k_ce_per_engine_service_any(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+       u32 intr_summary;
+
+       ath10k_pci_wake(ar);
+       intr_summary = CE_INTERRUPT_SUMMARY(ar);
+
+       for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
+               if (intr_summary & (1 << ce_id))
+                       intr_summary &= ~(1 << ce_id);
+               else
+                       /* no intr pending on this CE */
+                       continue;
+
+               ath10k_ce_per_engine_service(ar, ce_id);
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Adjust interrupts for the copy complete handler.
+ * If it's needed for either send or recv, then unmask
+ * this interrupt; otherwise, mask it.
+ *
+ * Called with ce_lock held.
+ */
+static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+                                               int disable_copy_compl_intr)
+{
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+
+       ath10k_pci_wake(ar);
+
+       if ((!disable_copy_compl_intr) &&
+           (ce_state->send_cb || ce_state->recv_cb))
+               ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr);
+       else
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+
+       ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_disable_interrupts(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+
+       ath10k_pci_wake(ar);
+       for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
+               struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+               u32 ctrl_addr = ce_state->ctrl_addr;
+
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+       }
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->send_cb = send_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags))
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->recv_cb = recv_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, 0);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+static int ath10k_ce_init_src_ring(struct ath10k *ar,
+                                  unsigned int ce_id,
+                                  struct ce_state *ce_state,
+                                  const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *src_ring;
+       unsigned int nentries = attr->src_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->src_ring) {
+               WARN_ON(ce_state->src_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->src_ring = (struct ce_ring_state *)ptr;
+       src_ring = ce_state->src_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       src_ring->nentries = nentries;
+       src_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       src_ring->hw_index = src_ring->sw_index;
+
+       src_ring->write_index =
+               ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       src_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       src_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       src_ring->base_addr_ce_space_unaligned = base_addr;
+
+       src_ring->base_addr_owner_space = PTR_ALIGN(
+                       src_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       src_ring->base_addr_ce_space = ALIGN(
+                       src_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       /*
+        * Also allocate a shadow src ring in regular
+        * mem to use for faster access.
+        */
+       src_ring->shadow_base_unaligned =
+               kmalloc((nentries * sizeof(struct ce_desc) +
+                        CE_DESC_RING_ALIGN), GFP_KERNEL);
+
+       src_ring->shadow_base = PTR_ALIGN(
+                       src_ring->shadow_base_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
+                                        src_ring->base_addr_ce_space);
+       ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
+       ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static int ath10k_ce_init_dest_ring(struct ath10k *ar,
+                                   unsigned int ce_id,
+                                   struct ce_state *ce_state,
+                                   const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries = attr->dest_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->dest_ring) {
+               WARN_ON(ce_state->dest_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->dest_ring = (struct ce_ring_state *)ptr;
+       dest_ring = ce_state->dest_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       dest_ring->nentries = nentries;
+       dest_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->write_index =
+               ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       dest_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       dest_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       dest_ring->base_addr_ce_space_unaligned = base_addr;
+
+       /*
+        * Correctly initialize memory to 0 to prevent garbage
+        * data crashing system when download firmware
+        */
+       memset(dest_ring->base_addr_owner_space_unaligned, 0,
+              nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN);
+
+       dest_ring->base_addr_owner_space = PTR_ALIGN(
+                       dest_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       dest_ring->base_addr_ce_space = ALIGN(
+                       dest_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
+                                         dest_ring->base_addr_ce_space);
+       ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+                                            unsigned int ce_id,
+                                            const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = NULL;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       if (!ar_pci->ce_id_to_state[ce_id]) {
+               ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
+               if (ce_state == NULL) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       return NULL;
+               }
+
+               ar_pci->ce_id_to_state[ce_id] = ce_state;
+               ce_state->ar = ar;
+               ce_state->id = ce_id;
+               ce_state->ctrl_addr = ctrl_addr;
+               ce_state->state = CE_RUNNING;
+               /* Save attribute flags */
+               ce_state->attr_flags = attr->flags;
+               ce_state->src_sz_max = attr->src_sz_max;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ce_state;
+}
+
+/*
+ * Initialize a Copy Engine based on caller-supplied attributes.
+ * This may be called once to initialize both source and destination
+ * rings or it may be called twice for separate source and destination
+ * initialization. It may be that only one side or the other is
+ * initialized by software/firmware.
+ */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr)
+{
+       struct ce_state *ce_state;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ce_state = ath10k_ce_init_state(ar, ce_id, attr);
+       if (!ce_state) {
+               ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
+               return NULL;
+       }
+
+       if (attr->src_nentries) {
+               if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE src ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       if (attr->dest_nentries) {
+               if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       /* Enable CE error interrupts */
+       ath10k_pci_wake(ar);
+       ath10k_ce_error_intr_enable(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       return ce_state;
+}
+
+void ath10k_ce_deinit(struct ce_state *ce_state)
+{
+       unsigned int ce_id = ce_state->id;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ce_state->state = CE_UNUSED;
+       ar_pci->ce_id_to_state[ce_id] = NULL;
+
+       if (ce_state->src_ring) {
+               kfree(ce_state->src_ring->shadow_base_unaligned);
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->src_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->src_ring->base_addr_owner_space,
+                                   ce_state->src_ring->base_addr_ce_space);
+               kfree(ce_state->src_ring);
+       }
+
+       if (ce_state->dest_ring) {
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->dest_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->dest_ring->base_addr_owner_space,
+                                   ce_state->dest_ring->base_addr_ce_space);
+               kfree(ce_state->dest_ring);
+       }
+       kfree(ce_state);
+}
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
new file mode 100644 (file)
index 0000000..c17f07c
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CE_H_
+#define _CE_H_
+
+#include "hif.h"
+
+
+/* Maximum number of Copy Engine's supported */
+#define CE_COUNT_MAX 8
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+
+/* Descriptor rings must be aligned to this boundary */
+#define CE_DESC_RING_ALIGN     8
+#define CE_SENDLIST_ITEMS_MAX  12
+#define CE_SEND_FLAG_GATHER    0x00010000
+
+/*
+ * Copy Engine support: low-level Target-side Copy Engine API.
+ * This is a hardware access layer used by code that understands
+ * how to use copy engines.
+ */
+
+struct ce_state;
+
+
+/* Copy Engine operational state */
+enum ce_op_state {
+       CE_UNUSED,
+       CE_PAUSED,
+       CE_RUNNING,
+};
+
+#define CE_DESC_FLAGS_GATHER         (1 << 0)
+#define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
+#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
+#define CE_DESC_FLAGS_META_DATA_LSB  3
+
+struct ce_desc {
+       __le32 addr;
+       __le16 nbytes;
+       __le16 flags; /* %CE_DESC_FLAGS_ */
+};
+
+/* Copy Engine Ring internal state */
+struct ce_ring_state {
+       /* Number of entries in this ring; must be power of 2 */
+       unsigned int nentries;
+       unsigned int nentries_mask;
+
+       /*
+        * For dest ring, this is the next index to be processed
+        * by software after it was/is received into.
+        *
+        * For src ring, this is the last descriptor that was sent
+        * and completion processed by software.
+        *
+        * Regardless of src or dest ring, this is an invariant
+        * (modulo ring size):
+        *     write index >= read index >= sw_index
+        */
+       unsigned int sw_index;
+       /* cached copy */
+       unsigned int write_index;
+       /*
+        * For src ring, this is the next index not yet processed by HW.
+        * This is a cached copy of the real HW index (read index), used
+        * for avoiding reading the HW index register more often than
+        * necessary.
+        * This extends the invariant:
+        *     write index >= read index >= hw_index >= sw_index
+        *
+        * For dest ring, this is currently unused.
+        */
+       /* cached copy */
+       unsigned int hw_index;
+
+       /* Start of DMA-coherent area reserved for descriptors */
+       /* Host address space */
+       void *base_addr_owner_space_unaligned;
+       /* CE address space */
+       u32 base_addr_ce_space_unaligned;
+
+       /*
+        * Actual start of descriptors.
+        * Aligned to descriptor-size boundary.
+        * Points into reserved DMA-coherent area, above.
+        */
+       /* Host address space */
+       void *base_addr_owner_space;
+
+       /* CE address space */
+       u32 base_addr_ce_space;
+       /*
+        * Start of shadow copy of descriptors, within regular memory.
+        * Aligned to descriptor-size boundary.
+        */
+       void *shadow_base_unaligned;
+       struct ce_desc *shadow_base;
+
+       void **per_transfer_context;
+};
+
+/* Copy Engine internal state */
+struct ce_state {
+       struct ath10k *ar;
+       unsigned int id;
+
+       unsigned int attr_flags;
+
+       u32 ctrl_addr;
+       enum ce_op_state state;
+
+       void (*send_cb) (struct ce_state *ce_state,
+                        void *per_transfer_send_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id);
+       void (*recv_cb) (struct ce_state *ce_state,
+                        void *per_transfer_recv_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id,
+                        unsigned int flags);
+
+       unsigned int src_sz_max;
+       struct ce_ring_state *src_ring;
+       struct ce_ring_state *dest_ring;
+};
+
+struct ce_sendlist_item {
+       /* e.g. buffer or desc list */
+       dma_addr_t data;
+       union {
+               /* simple buffer */
+               unsigned int nbytes;
+               /* Rx descriptor list */
+               unsigned int ndesc;
+       } u;
+       /* externally-specified flags; OR-ed with internal flags */
+       u32 flags;
+};
+
+struct ce_sendlist {
+       unsigned int num_items;
+       struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+};
+
+/* Copy Engine settable attributes */
+struct ce_attr;
+
+/*==================Send====================*/
+
+/* ath10k_ce_send flags */
+#define CE_SEND_FLAG_BYTE_SWAP 1
+
+/*
+ * Queue a source buffer to be sent to an anonymous destination buffer.
+ *   ce         - which copy engine to use
+ *   buffer          - address of buffer
+ *   nbytes          - number of bytes to send
+ *   transfer_id     - arbitrary ID; reflected to destination
+ *   flags           - CE_SEND_FLAG_* values
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Note: If no flags are specified, use CE's default data swap mode.
+ *
+ * Implementation note: pushes 1 buffer to Source ring
+ */
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_send_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  /* 14 bits */
+                  unsigned int transfer_id,
+                  unsigned int flags);
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts);
+
+/* Append a simple buffer (address/length) to a sendlist. */
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
+                               u32 buffer,
+                               unsigned int nbytes,
+                               /* OR-ed with internal flags */
+                               u32 flags);
+
+/*
+ * Queue a "sendlist" of buffers to be sent using gather to a single
+ * anonymous destination buffer
+ *   ce         - which copy engine to use
+ *   sendlist        - list of simple buffers to send using gather
+ *   transfer_id     - arbitrary ID; reflected to destination
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes multiple buffers with Gather to Source ring.
+ */
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_send_context,
+                           struct ce_sendlist *sendlist,
+                           /* 14 bits */
+                           unsigned int transfer_id);
+
+/*==================Recv=======================*/
+
+/*
+ * Make a buffer available to receive. The buffer must be at least of a
+ * minimal size appropriate for this copy engine (src_sz_max attribute).
+ *   ce                    - which copy engine to use
+ *   per_transfer_recv_context  - context passed back to caller's recv_cb
+ *   buffer                     - address of buffer in CE space
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes a buffer to Dest ring.
+ */
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_transfer_recv_context,
+                              u32 buffer);
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags));
+
+/* recv flags */
+/* Data is byte-swapped */
+#define CE_RECV_FLAG_SWAPPED   1
+
+/*
+ * Supply data for the next completed unprocessed receive descriptor.
+ * Pops buffer from Dest ring.
+ */
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp);
+/*
+ * Supply data for the next completed unprocessed send descriptor.
+ * Pops 1 completed send buffer from Source ring.
+ */
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                          void **per_transfer_contextp,
+                          u32 *bufferp,
+                          unsigned int *nbytesp,
+                          unsigned int *transfer_idp);
+
+/*==================CE Engine Initialization=======================*/
+
+/* Initialize an instance of a CE */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr);
+
+/*==================CE Engine Shutdown=======================*/
+/*
+ * Support clean shutdown by allowing the caller to revoke
+ * receive buffers.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp);
+
+/*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp);
+
+void ath10k_ce_deinit(struct ce_state *ce_state);
+
+/*==================CE Interrupt Handlers====================*/
+void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+void ath10k_ce_disable_interrupts(struct ath10k *ar);
+
+/* ce_attr.flags values */
+/* Use NonSnooping PCIe accesses? */
+#define CE_ATTR_NO_SNOOP               1
+
+/* Byte swap data words */
+#define CE_ATTR_BYTE_SWAP_DATA         2
+
+/* Swizzle descriptors? */
+#define CE_ATTR_SWIZZLE_DESCRIPTORS    4
+
+/* no interrupt on copy completion */
+#define CE_ATTR_DIS_INTR               8
+
+/* Attributes of an instance of a Copy Engine */
+struct ce_attr {
+       /* CE_ATTR_* values */
+       unsigned int flags;
+
+       /* currently not in use */
+       unsigned int priority;
+
+       /* #entries in source ring - Must be a power of 2 */
+       unsigned int src_nentries;
+
+       /*
+        * Max source send size for this CE.
+        * This is also the minimum size of a destination buffer.
+        */
+       unsigned int src_sz_max;
+
+       /* #entries in destination ring - Must be a power of 2 */
+       unsigned int dest_nentries;
+
+       /* Future use */
+       void *reserved;
+};
+
+/*
+ * When using sendlist_send to transfer multiple buffer fragments, the
+ * transfer context of each fragment, except last one, will be filled
+ * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
+ * each fragment done with send and the transfer context would be
+ * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
+ * status of a send completion.
+ */
+#define CE_SENDLIST_ITEM_CTXT  ((void *)0xcecebeef)
+
+#define SR_BA_ADDRESS          0x0000
+#define SR_SIZE_ADDRESS                0x0004
+#define DR_BA_ADDRESS          0x0008
+#define DR_SIZE_ADDRESS                0x000c
+#define CE_CMD_ADDRESS         0x0018
+
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK    0x00020000
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
+       CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK    0x00010000
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
+       (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_DMAX_LENGTH_MSB               15
+#define CE_CTRL1_DMAX_LENGTH_LSB               0
+#define CE_CTRL1_DMAX_LENGTH_MASK              0x0000ffff
+#define CE_CTRL1_DMAX_LENGTH_GET(x) \
+       (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
+#define CE_CTRL1_DMAX_LENGTH_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
+
+#define CE_CTRL1_ADDRESS                       0x0010
+#define CE_CTRL1_HW_MASK                       0x0007ffff
+#define CE_CTRL1_SW_MASK                       0x0007ffff
+#define CE_CTRL1_HW_WRITE_MASK                 0x00000000
+#define CE_CTRL1_SW_WRITE_MASK                 0x0007ffff
+#define CE_CTRL1_RSTMASK                       0xffffffff
+#define CE_CTRL1_RESET                         0x00000080
+
+#define CE_CMD_HALT_STATUS_MSB                 3
+#define CE_CMD_HALT_STATUS_LSB                 3
+#define CE_CMD_HALT_STATUS_MASK                        0x00000008
+#define CE_CMD_HALT_STATUS_GET(x) \
+       (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
+#define CE_CMD_HALT_STATUS_SET(x) \
+       (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
+#define CE_CMD_HALT_STATUS_RESET               0
+#define CE_CMD_HALT_MSB                                0
+#define CE_CMD_HALT_MASK                       0x00000001
+
+#define HOST_IE_COPY_COMPLETE_MSB              0
+#define HOST_IE_COPY_COMPLETE_LSB              0
+#define HOST_IE_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IE_COPY_COMPLETE_GET(x) \
+       (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
+#define HOST_IE_COPY_COMPLETE_SET(x) \
+       (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
+#define HOST_IE_COPY_COMPLETE_RESET            0
+#define HOST_IE_ADDRESS                                0x002c
+
+#define HOST_IS_DST_RING_LOW_WATERMARK_MASK    0x00000010
+#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK   0x00000008
+#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK    0x00000004
+#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK   0x00000002
+#define HOST_IS_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IS_ADDRESS                                0x0030
+
+#define MISC_IE_ADDRESS                                0x0034
+
+#define MISC_IS_AXI_ERR_MASK                   0x00000400
+
+#define MISC_IS_DST_ADDR_ERR_MASK              0x00000200
+#define MISC_IS_SRC_LEN_ERR_MASK               0x00000100
+#define MISC_IS_DST_MAX_LEN_VIO_MASK           0x00000080
+#define MISC_IS_DST_RING_OVERFLOW_MASK         0x00000040
+#define MISC_IS_SRC_RING_OVERFLOW_MASK         0x00000020
+
+#define MISC_IS_ADDRESS                                0x0038
+
+#define SR_WR_INDEX_ADDRESS                    0x003c
+
+#define DST_WR_INDEX_ADDRESS                   0x0040
+
+#define CURRENT_SRRI_ADDRESS                   0x0044
+
+#define CURRENT_DRRI_ADDRESS                   0x0048
+
+#define SRC_WATERMARK_LOW_MSB                  31
+#define SRC_WATERMARK_LOW_LSB                  16
+#define SRC_WATERMARK_LOW_MASK                 0xffff0000
+#define SRC_WATERMARK_LOW_GET(x) \
+       (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
+#define SRC_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_LOW_RESET                        0
+#define SRC_WATERMARK_HIGH_MSB                 15
+#define SRC_WATERMARK_HIGH_LSB                 0
+#define SRC_WATERMARK_HIGH_MASK                        0x0000ffff
+#define SRC_WATERMARK_HIGH_GET(x) \
+       (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
+#define SRC_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
+#define SRC_WATERMARK_HIGH_RESET               0
+#define SRC_WATERMARK_ADDRESS                  0x004c
+
+#define DST_WATERMARK_LOW_LSB                  16
+#define DST_WATERMARK_LOW_MASK                 0xffff0000
+#define DST_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_LOW_RESET                        0
+#define DST_WATERMARK_HIGH_MSB                 15
+#define DST_WATERMARK_HIGH_LSB                 0
+#define DST_WATERMARK_HIGH_MASK                        0x0000ffff
+#define DST_WATERMARK_HIGH_GET(x) \
+       (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
+#define DST_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_HIGH_RESET               0
+#define DST_WATERMARK_ADDRESS                  0x0050
+
+
+static inline u32 ath10k_ce_base_address(unsigned int ce_id)
+{
+       return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
+}
+
+#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
+                          HOST_IS_DST_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+
+#define CE_ERROR_MASK  (MISC_IS_AXI_ERR_MASK           | \
+                        MISC_IS_DST_ADDR_ERR_MASK      | \
+                        MISC_IS_SRC_LEN_ERR_MASK       | \
+                        MISC_IS_DST_MAX_LEN_VIO_MASK   | \
+                        MISC_IS_DST_RING_OVERFLOW_MASK | \
+                        MISC_IS_SRC_RING_OVERFLOW_MASK)
+
+#define CE_SRC_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+#define CE_DEST_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
+#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
+       (((int)(toidx)-(int)(fromidx)) & (nentries_mask))
+
+#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
+
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB              8
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK             0x0000ff00
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
+       (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+               CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS                   0x0000
+
+#define CE_INTERRUPT_SUMMARY(ar) \
+       CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
+               ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+               CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
+
+#endif /* _CE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
new file mode 100644 (file)
index 0000000..2b3426b
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+
+#include "core.h"
+#include "mac.h"
+#include "htc.h"
+#include "hif.h"
+#include "wmi.h"
+#include "bmi.h"
+#include "debug.h"
+#include "htt.h"
+
+unsigned int ath10k_debug_mask;
+static bool uart_print;
+static unsigned int ath10k_p2p;
+module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+module_param(uart_print, bool, 0644);
+module_param_named(p2p, ath10k_p2p, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+MODULE_PARM_DESC(uart_print, "Uart target debugging");
+MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+
+static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+       {
+               .id = QCA988X_HW_1_0_VERSION,
+               .name = "qca988x hw1.0",
+               .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_1_0_FW_DIR,
+                       .fw = QCA988X_HW_1_0_FW_FILE,
+                       .otp = QCA988X_HW_1_0_OTP_FILE,
+                       .board = QCA988X_HW_1_0_BOARD_DATA_FILE,
+               },
+       },
+       {
+               .id = QCA988X_HW_2_0_VERSION,
+               .name = "qca988x hw2.0",
+               .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_2_0_FW_DIR,
+                       .fw = QCA988X_HW_2_0_FW_FILE,
+                       .otp = QCA988X_HW_2_0_OTP_FILE,
+                       .board = QCA988X_HW_2_0_BOARD_DATA_FILE,
+               },
+       },
+};
+
+static void ath10k_send_suspend_complete(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+
+       ar->is_target_paused = true;
+       wake_up(&ar->event_queue);
+}
+
+static int ath10k_check_fw_version(struct ath10k *ar)
+{
+       char version[32];
+
+       if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
+           ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
+           ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
+           ar->fw_version_build >= SUPPORTED_FW_BUILD)
+               return 0;
+
+       snprintf(version, sizeof(version), "%u.%u.%u.%u",
+                SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
+                SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
+
+       ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
+                   ar->hw->wiphy->fw_version);
+       ath10k_warn("Please upgrade to version %s (or newer)\n", version);
+
+       return 0;
+}
+
+static int ath10k_init_connect_htc(struct ath10k *ar)
+{
+       int status;
+
+       status = ath10k_wmi_connect_htc_service(ar);
+       if (status)
+               goto conn_fail;
+
+       /* Start HTC */
+       status = ath10k_htc_start(ar->htc);
+       if (status)
+               goto conn_fail;
+
+       /* Wait for WMI event to be ready */
+       status = ath10k_wmi_wait_for_service_ready(ar);
+       if (status <= 0) {
+               ath10k_warn("wmi service ready event not received");
+               status = -ETIMEDOUT;
+               goto timeout;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+       return 0;
+
+timeout:
+       ath10k_htc_stop(ar->htc);
+conn_fail:
+       return status;
+}
+
+static int ath10k_init_configure_target(struct ath10k *ar)
+{
+       u32 param_host;
+       int ret;
+
+       /* tell target which HTC version it is used*/
+       ret = ath10k_bmi_write32(ar, hi_app_host_interest,
+                                HTC_PROTOCOL_VERSION);
+       if (ret) {
+               ath10k_err("settings HTC version failed\n");
+               return ret;
+       }
+
+       /* set the firmware mode to STA/IBSS/AP */
+       ret = ath10k_bmi_read32(ar, hi_option_flag, &param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (1/2) failed\n");
+               return ret;
+       }
+
+       /* TODO following parameters need to be re-visited. */
+       /* num_device */
+       param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+       /* Firmware mode */
+       /* FIXME: Why FW_MODE_AP ??.*/
+       param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT);
+       /* mac_addr_method */
+       param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+       /* firmware_bridge */
+       param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+       /* fwsubmode */
+       param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT);
+
+       ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (2/2) failed\n");
+               return ret;
+       }
+
+       /* We do all byte-swapping on the host */
+       ret = ath10k_bmi_write32(ar, hi_be, 0);
+       if (ret) {
+               ath10k_err("setting host CPU BE mode failed\n");
+               return ret;
+       }
+
+       /* FW descriptor/Data swap flags */
+       ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);
+
+       if (ret) {
+               ath10k_err("setting FW data/desc swap flags failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+                                                  const char *dir,
+                                                  const char *file)
+{
+       char filename[100];
+       const struct firmware *fw;
+       int ret;
+
+       if (file == NULL)
+               return ERR_PTR(-ENOENT);
+
+       if (dir == NULL)
+               dir = ".";
+
+       snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+       ret = request_firmware(&fw, filename, ar->dev);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return fw;
+}
+
+static int ath10k_push_board_ext_data(struct ath10k *ar,
+                                     const struct firmware *fw)
+{
+       u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+       u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
+       u32 board_ext_data_addr;
+       int ret;
+
+       ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
+       if (ret) {
+               ath10k_err("could not read board ext data addr (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "ath10k: Board extended Data download addr: 0x%x\n",
+                  board_ext_data_addr);
+
+       if (board_ext_data_addr == 0)
+               return 0;
+
+       if (fw->size != (board_data_size + board_ext_data_size)) {
+               ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
+                          fw->size, board_data_size, board_ext_data_size);
+               return -EINVAL;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
+                                     fw->data + board_data_size,
+                                     board_ext_data_size);
+       if (ret) {
+               ath10k_err("could not write board ext data (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
+                                (board_ext_data_size << 16) | 1);
+       if (ret) {
+               ath10k_err("could not write board ext data bit (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_download_board_data(struct ath10k *ar)
+{
+       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);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_read32(ar, hi_board_data, &address);
+       if (ret) {
+               ath10k_err("could not read board data addr (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, address, fw->data,
+                                     min_t(u32, board_data_size, fw->size));
+       if (ret) {
+               ath10k_err("could not write board data (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
+       if (ret) {
+               ath10k_err("could not write board data bit (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_and_run_otp(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       u32 address;
+       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));
+               return 0;
+       }
+
+       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       if (ret) {
+               ath10k_err("could not write otp (%d)\n", ret);
+               goto exit;
+       }
+
+       exec_param = 0;
+       ret = ath10k_bmi_execute(ar, address, &exec_param);
+       if (ret) {
+               ath10k_err("could not execute otp (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_fw(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       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);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_init_download_firmware(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_download_board_data(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_and_run_otp(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_fw(ar);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int ath10k_init_uart(struct ath10k *ar)
+{
+       int ret;
+
+       /*
+        * Explicitly setting UART prints to zero as target turns it on
+        * based on scratch registers.
+        */
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
+       if (ret) {
+               ath10k_warn("could not disable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       if (!uart_print) {
+               ath10k_info("UART prints disabled\n");
+               return 0;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_info("UART prints enabled\n");
+       return 0;
+}
+
+static int ath10k_init_hw_params(struct ath10k *ar)
+{
+       const struct ath10k_hw_params *uninitialized_var(hw_params);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
+               hw_params = &ath10k_hw_params_list[i];
+
+               if (hw_params->id == ar->target_version)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
+               ath10k_err("Unsupported hardware version: 0x%x\n",
+                          ar->target_version);
+               return -EINVAL;
+       }
+
+       ar->hw_params = *hw_params;
+
+       ath10k_info("Hardware name %s version 0x%x\n",
+                   ar->hw_params.name, ar->target_version);
+
+       return 0;
+}
+
+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;
+
+       ar = ath10k_mac_create();
+       if (!ar)
+               return NULL;
+
+       ar->ath_common.priv = ar;
+       ar->ath_common.hw = ar->hw;
+
+       ar->p2p = !!ath10k_p2p;
+       ar->dev = 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);
+       init_completion(&ar->scan.on_channel);
+
+       init_completion(&ar->install_key_done);
+       init_completion(&ar->vdev_setup_done);
+
+       setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+
+       ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+       if (!ar->workqueue)
+               goto err_wq;
+
+       mutex_init(&ar->conf_mutex);
+       spin_lock_init(&ar->data_lock);
+
+       INIT_LIST_HEAD(&ar->peers);
+       init_waitqueue_head(&ar->peer_mapping_wq);
+
+       init_completion(&ar->offchan_tx_completed);
+       INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
+       skb_queue_head_init(&ar->offchan_tx_queue);
+
+       init_waitqueue_head(&ar->event_queue);
+
+       return ar;
+
+err_wq:
+       ath10k_mac_destroy(ar);
+       return NULL;
+}
+EXPORT_SYMBOL(ath10k_core_create);
+
+void ath10k_core_destroy(struct ath10k *ar)
+{
+       flush_workqueue(ar->workqueue);
+       destroy_workqueue(ar->workqueue);
+
+       ath10k_mac_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_destroy);
+
+
+int ath10k_core_register(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;
+
+       if (ath10k_init_configure_target(ar)) {
+               status = -EINVAL;
+               goto err;
+       }
+
+       status = ath10k_init_download_firmware(ar);
+       if (status)
+               goto err;
+
+       status = ath10k_init_uart(ar);
+       if (status)
+               goto err;
+
+       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);
+               goto err;
+       }
+
+       status = ath10k_bmi_done(ar);
+       if (status)
+               goto err_htc_destroy;
+
+       status = ath10k_wmi_attach(ar);
+       if (status) {
+               ath10k_err("WMI attach failed: %d\n", status);
+               goto err_htc_destroy;
+       }
+
+       status = ath10k_htc_wait_target(ar->htc);
+       if (status)
+               goto err_wmi_detach;
+
+       ar->htt = ath10k_htt_attach(ar);
+       if (!ar->htt) {
+               status = -ENOMEM;
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_init_connect_htc(ar);
+       if (status)
+               goto err_htt_detach;
+
+       ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
+
+       status = ath10k_check_fw_version(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_wmi_cmd_init(ar);
+       if (status) {
+               ath10k_err("could not send WMI init command (%d)\n", status);
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_wmi_wait_for_unified_ready(ar);
+       if (status <= 0) {
+               ath10k_err("wmi unified ready event not received\n");
+               status = -ETIMEDOUT;
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_htt_attach_target(ar->htt);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_mac_register(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       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_disconnect_htc:
+       ath10k_htc_stop(ar->htc);
+err_htt_detach:
+       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);
+
+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_htc_stop(ar->htc);
+       ath10k_htt_detach(ar->htt);
+       ath10k_wmi_detach(ar);
+       ath10k_htc_destroy(ar->htc);
+}
+EXPORT_SYMBOL(ath10k_core_unregister);
+
+int ath10k_core_target_suspend(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret)
+               ath10k_warn("could not suspend target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_suspend);
+
+int ath10k_core_target_resume(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_resume);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
new file mode 100644 (file)
index 0000000..539336d
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "htc.h"
+#include "hw.h"
+#include "targaddrs.h"
+#include "wmi.h"
+#include "../ath.h"
+#include "../regd.h"
+
+#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
+#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
+#define WO(_f)      ((_f##_OFFSET) >> 2)
+
+#define ATH10K_SCAN_ID 0
+#define WMI_READY_TIMEOUT (5 * HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+
+/* 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;
+       bool is_aborted;
+
+       struct {
+               u8 vdev_id;
+               u16 msdu_id;
+               u8 tid;
+               bool is_offchan;
+               bool is_conf;
+               bool discard;
+               bool no_ack;
+               u8 refcount;
+               struct sk_buff *txfrag;
+               struct sk_buff *msdu;
+       } __packed htt;
+
+       /* 4 bytes left on 64bit arch */
+} __packed;
+
+static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
+                    IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+       return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
+}
+
+static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
+{
+       if (ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
+                                                  DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
+               return -EIO;
+
+       ATH10K_SKB_CB(skb)->is_mapped = true;
+       return 0;
+}
+
+static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
+                        DMA_TO_DEVICE);
+       ATH10K_SKB_CB(skb)->is_mapped = false;
+       return 0;
+}
+
+static inline u32 host_interest_item_address(u32 item_offset)
+{
+       return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
+}
+
+struct ath10k_bmi {
+       bool done_sent;
+};
+
+struct ath10k_wmi {
+       enum ath10k_htc_ep_id eid;
+       struct completion service_ready;
+       struct completion unified_ready;
+       atomic_t pending_tx_count;
+       wait_queue_head_t wq;
+
+       struct sk_buff_head wmi_event_list;
+       struct work_struct wmi_event_work;
+};
+
+struct ath10k_peer_stat {
+       u8 peer_macaddr[ETH_ALEN];
+       u32 peer_rssi;
+       u32 peer_tx_rate;
+};
+
+struct ath10k_target_stats {
+       /* PDEV stats */
+       s32 ch_noise_floor;
+       u32 tx_frame_count;
+       u32 rx_frame_count;
+       u32 rx_clear_count;
+       u32 cycle_count;
+       u32 phy_err_count;
+       u32 chan_tx_power;
+
+       /* PDEV TX stats */
+       s32 comp_queued;
+       s32 comp_delivered;
+       s32 msdu_enqued;
+       s32 mpdu_enqued;
+       s32 wmm_drop;
+       s32 local_enqued;
+       s32 local_freed;
+       s32 hw_queued;
+       s32 hw_reaped;
+       s32 underrun;
+       s32 tx_abort;
+       s32 mpdus_requed;
+       u32 tx_ko;
+       u32 data_rc;
+       u32 self_triggers;
+       u32 sw_retry_failure;
+       u32 illgl_rate_phy_err;
+       u32 pdev_cont_xretry;
+       u32 pdev_tx_timeout;
+       u32 pdev_resets;
+       u32 phy_underrun;
+       u32 txop_ovf;
+
+       /* PDEV RX stats */
+       s32 mid_ppdu_route_change;
+       s32 status_rcvd;
+       s32 r0_frags;
+       s32 r1_frags;
+       s32 r2_frags;
+       s32 r3_frags;
+       s32 htt_msdus;
+       s32 htt_mpdus;
+       s32 loc_msdus;
+       s32 loc_mpdus;
+       s32 oversize_amsdu;
+       s32 phy_errs;
+       s32 phy_err_drop;
+       s32 mpdu_errs;
+
+       /* VDEV STATS */
+
+       /* PEER STATS */
+       u8 peers;
+       struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+
+       /* TODO: Beacon filter stats */
+
+};
+
+#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
+
+struct ath10k_peer {
+       struct list_head list;
+       int vdev_id;
+       u8 addr[ETH_ALEN];
+       DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+       struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+};
+
+#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
+
+struct ath10k_vif {
+       u32 vdev_id;
+       enum wmi_vdev_type vdev_type;
+       enum wmi_vdev_subtype vdev_subtype;
+       u32 beacon_interval;
+       u32 dtim_period;
+
+       struct ath10k *ar;
+       struct ieee80211_vif *vif;
+
+       struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
+       u8 def_wep_key_index;
+
+       u16 tx_seq_no;
+
+       union {
+               struct {
+                       u8 bssid[ETH_ALEN];
+                       u32 uapsd;
+               } sta;
+               struct {
+                       /* 127 stations; wmi limit */
+                       u8 tim_bitmap[16];
+                       u8 tim_len;
+                       u32 ssid_len;
+                       u8 ssid[IEEE80211_MAX_SSID_LEN];
+                       bool hidden_ssid;
+                       /* P2P_IE with NoA attribute for P2P_GO case */
+                       u32 noa_len;
+                       u8 *noa_data;
+               } ap;
+               struct {
+                       u8 bssid[ETH_ALEN];
+               } ibss;
+       } u;
+};
+
+struct ath10k_vif_iter {
+       u32 vdev_id;
+       struct ath10k_vif *arvif;
+};
+
+struct ath10k_debug {
+       struct dentry *debugfs_phy;
+
+       struct ath10k_target_stats target_stats;
+       u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+
+       struct completion event_stats_compl;
+};
+
+struct ath10k {
+       struct ath_common ath_common;
+       struct ieee80211_hw *hw;
+       struct device *dev;
+       u8 mac_addr[ETH_ALEN];
+
+       u32 target_version;
+       u8 fw_version_major;
+       u32 fw_version_minor;
+       u16 fw_version_release;
+       u16 fw_version_build;
+       u32 phy_capability;
+       u32 hw_min_tx_power;
+       u32 hw_max_tx_power;
+       u32 ht_cap_info;
+       u32 vht_cap_info;
+
+       struct targetdef *targetdef;
+       struct hostdef *hostdef;
+
+       bool p2p;
+
+       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_hw_params {
+               u32 id;
+               const char *name;
+               u32 patch_load_addr;
+
+               struct ath10k_hw_params_fw {
+                       const char *dir;
+                       const char *fw;
+                       const char *otp;
+                       const char *board;
+               } fw;
+       } hw_params;
+
+       struct {
+               struct completion started;
+               struct completion completed;
+               struct completion on_channel;
+               struct timer_list timeout;
+               bool is_roc;
+               bool in_progress;
+               bool aborting;
+               int vdev_id;
+               int roc_freq;
+       } scan;
+
+       struct {
+               struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+       } mac;
+
+       /* should never be NULL; needed for regular htt rx */
+       struct ieee80211_channel *rx_channel;
+
+       /* valid during scan; needed for mgmt rx during scan */
+       struct ieee80211_channel *scan_channel;
+
+       int free_vdev_map;
+       int monitor_vdev_id;
+       bool monitor_enabled;
+       bool monitor_present;
+       unsigned int filter_flags;
+
+       struct wmi_pdev_set_wmm_params_arg wmm_params;
+       struct completion install_key_done;
+
+       struct completion vdev_setup_done;
+
+       struct workqueue_struct *workqueue;
+
+       /* prevents concurrent FW reconfiguration */
+       struct mutex conf_mutex;
+
+       /* protects shared structure data */
+       spinlock_t data_lock;
+
+       struct list_head peers;
+       wait_queue_head_t peer_mapping_wq;
+
+       struct work_struct offchan_tx_work;
+       struct sk_buff_head offchan_tx_queue;
+       struct completion offchan_tx_completed;
+       struct sk_buff *offchan_tx_skb;
+
+#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_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_ */
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
new file mode 100644 (file)
index 0000000..499034b
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_printk(const char *level, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       int rtn;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       rtn = printk("%sath10k: %pV", level, &vaf);
+
+       va_end(args);
+
+       return rtn;
+}
+
+int ath10k_info(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+       trace_ath10k_log_info(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_info);
+
+int ath10k_err(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+       trace_ath10k_log_err(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_err);
+
+int ath10k_warn(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret = 0;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+
+       if (net_ratelimit())
+               ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+
+       trace_ath10k_log_warn(&vaf);
+
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_warn);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size)
+{
+       memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+}
+
+static ssize_t ath10k_read_wmi_services(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char *buf;
+       unsigned int len = 0, buf_len = 1500;
+       const char *status;
+       ssize_t ret_cnt;
+       int i;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (len > buf_len)
+               len = buf_len;
+
+       for (i = 0; i < WMI_SERVICE_LAST; i++) {
+               if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+                       status = "enabled";
+               else
+                       status = "disabled";
+
+               len += scnprintf(buf + len, buf_len - len,
+                                "0x%02x - %20s - %s\n",
+                                i, wmi_service_name(i), status);
+       }
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_wmi_services = {
+       .read = ath10k_read_wmi_services,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev)
+{
+       u8 *tmp = ev->data;
+       struct ath10k_target_stats *stats;
+       int num_pdev_stats, num_vdev_stats, num_peer_stats;
+       struct wmi_pdev_stats *ps;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       stats = &ar->debug.target_stats;
+
+       num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+       num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+       num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+
+       if (num_pdev_stats) {
+               ps = (struct wmi_pdev_stats *)tmp;
+
+               stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+               stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+               stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+               stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+               stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+               stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+               stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+
+               stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+               stats->comp_delivered =
+                       __le32_to_cpu(ps->wal.tx.comp_delivered);
+               stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+               stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+               stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+               stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+               stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+               stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+               stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+               stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+               stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+               stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+               stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+               stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+               stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+               stats->sw_retry_failure =
+                       __le32_to_cpu(ps->wal.tx.sw_retry_failure);
+               stats->illgl_rate_phy_err =
+                       __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+               stats->pdev_cont_xretry =
+                       __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+               stats->pdev_tx_timeout =
+                       __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+               stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+               stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+               stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+
+               stats->mid_ppdu_route_change =
+                       __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+               stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+               stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+               stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+               stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+               stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+               stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+               stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+               stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+               stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+               stats->oversize_amsdu =
+                       __le32_to_cpu(ps->wal.rx.oversize_amsdu);
+               stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+               stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+               stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+
+               tmp += sizeof(struct wmi_pdev_stats);
+       }
+
+       /* 0 or max vdevs */
+       /* Currently firmware does not support VDEV stats */
+       if (num_vdev_stats) {
+               struct wmi_vdev_stats *vdev_stats;
+
+               for (i = 0; i < num_vdev_stats; i++) {
+                       vdev_stats = (struct wmi_vdev_stats *)tmp;
+                       tmp += sizeof(struct wmi_vdev_stats);
+               }
+       }
+
+       if (num_peer_stats) {
+               struct wmi_peer_stats *peer_stats;
+               struct ath10k_peer_stat *s;
+
+               stats->peers = num_peer_stats;
+
+               for (i = 0; i < num_peer_stats; i++) {
+                       peer_stats = (struct wmi_peer_stats *)tmp;
+                       s = &stats->peer_stat[i];
+
+                       WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
+                                                  s->peer_macaddr);
+                       s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+                       s->peer_tx_rate =
+                               __le32_to_cpu(peer_stats->peer_tx_rate);
+
+                       tmp += sizeof(struct wmi_peer_stats);
+               }
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       complete(&ar->debug.event_stats_compl);
+}
+
+static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       struct ath10k_target_stats *fw_stats;
+       char *buf;
+       unsigned int len = 0, buf_len = 2500;
+       ssize_t ret_cnt;
+       long left;
+       int i;
+       int ret;
+
+       fw_stats = &ar->debug.target_stats;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       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;
+       }
+
+       left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+
+       if (left <= 0) {
+               kfree(buf);
+               return -ETIMEDOUT;
+       }
+
+       mutex_lock(&ar->conf_mutex);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Channel noise floor", fw_stats->ch_noise_floor);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Channel TX power", fw_stats->chan_tx_power);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "TX frame count", fw_stats->tx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX frame count", fw_stats->rx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX clear count", fw_stats->rx_clear_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Cycle count", fw_stats->cycle_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "PHY error count", fw_stats->phy_err_count);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV TX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies queued", fw_stats->comp_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies disp.", fw_stats->comp_delivered);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDU queued", fw_stats->msdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU queued", fw_stats->mpdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs dropped", fw_stats->wmm_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local enqued", fw_stats->local_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local freed", fw_stats->local_freed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW queued", fw_stats->hw_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs reaped", fw_stats->hw_reaped);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Num underruns", fw_stats->underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs cleaned", fw_stats->tx_abort);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs requed", fw_stats->mpdus_requed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Excessive retries", fw_stats->tx_ko);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW rate", fw_stats->data_rc);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Sched self tiggers", fw_stats->self_triggers);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Dropped due to SW retries",
+                        fw_stats->sw_retry_failure);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Illegal rate phy errors",
+                        fw_stats->illgl_rate_phy_err);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "TX timeout", fw_stats->pdev_tx_timeout);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PDEV resets", fw_stats->pdev_resets);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY underrun", fw_stats->phy_underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU is more than txop limit", fw_stats->txop_ovf);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV RX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Mid PPDU route change",
+                        fw_stats->mid_ppdu_route_change);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Tot. number of statuses", fw_stats->status_rcvd);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 0", fw_stats->r0_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 1", fw_stats->r1_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 2", fw_stats->r2_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 3", fw_stats->r3_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to HTT", fw_stats->htt_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to stack", fw_stats->loc_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to stack", fw_stats->loc_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Oversized AMSUs", fw_stats->oversize_amsdu);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors", fw_stats->phy_errs);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors drops", fw_stats->phy_err_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PEER stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       for (i = 0; i < fw_stats->peers; i++) {
+               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                                "Peer MAC address",
+                                fw_stats->peer_stat[i].peer_macaddr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer TX rate",
+                                fw_stats->peer_stat[i].peer_tx_rate);
+               len += scnprintf(buf + len, buf_len - len, "\n");
+       }
+
+       if (len > buf_len)
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_fw_stats = {
+       .read = ath10k_read_fw_stats,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+int ath10k_debug_create(struct ath10k *ar)
+{
+       ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+                                                  ar->hw->wiphy->debugfsdir);
+
+       if (!ar->debug.debugfs_phy)
+               return -ENOMEM;
+
+       init_completion(&ar->debug.event_stats_compl);
+
+       debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_fw_stats);
+
+       debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_wmi_services);
+
+       return 0;
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       if (ath10k_debug_mask & mask)
+               ath10k_printk(KERN_DEBUG, "%pV", &vaf);
+
+       trace_ath10k_log_dbg(mask, &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL(ath10k_dbg);
+
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len)
+{
+       if (ath10k_debug_mask & mask) {
+               if (msg)
+                       ath10k_dbg(mask, "%s\n", msg);
+
+               print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+       }
+
+       /* tracing code doesn't like null strings :/ */
+       trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+                                 buf, len);
+}
+EXPORT_SYMBOL(ath10k_dbg_dump);
+
+#endif /* CONFIG_ATH10K_DEBUG */
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
new file mode 100644 (file)
index 0000000..168140c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include <linux/types.h>
+#include "trace.h"
+
+enum ath10k_debug_mask {
+       ATH10K_DBG_PCI          = 0x00000001,
+       ATH10K_DBG_WMI          = 0x00000002,
+       ATH10K_DBG_HTC          = 0x00000004,
+       ATH10K_DBG_HTT          = 0x00000008,
+       ATH10K_DBG_MAC          = 0x00000010,
+       ATH10K_DBG_CORE         = 0x00000020,
+       ATH10K_DBG_PCI_DUMP     = 0x00000040,
+       ATH10K_DBG_HTT_DUMP     = 0x00000080,
+       ATH10K_DBG_MGMT         = 0x00000100,
+       ATH10K_DBG_DATA         = 0x00000200,
+       ATH10K_DBG_ANY          = 0xffffffff,
+};
+
+extern unsigned int ath10k_debug_mask;
+
+extern __printf(1, 2) int ath10k_info(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size);
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev);
+
+#else
+static inline int ath10k_debug_create(struct ath10k *ar)
+{
+       return 0;
+}
+
+static inline void ath10k_debug_read_service_map(struct ath10k *ar,
+                                                void *service_map,
+                                                size_t map_size)
+{
+}
+
+static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                                 struct wmi_stats_event *ev)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
+                                     const char *fmt, ...);
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len);
+#else /* CONFIG_ATH10K_DEBUG */
+
+static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask,
+                            const char *fmt, ...)
+{
+       return 0;
+}
+
+static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                                  const char *msg, const char *prefix,
+                                  const void *buf, size_t len)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUG */
+#endif /* _DEBUG_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
new file mode 100644 (file)
index 0000000..73a24d4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HIF_H_
+#define _HIF_H_
+
+#include <linux/kernel.h>
+#include "core.h"
+
+struct ath10k_hif_cb {
+       int (*tx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            unsigned transfer_id);
+       int (*rx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            u8 pipe_id);
+};
+
+struct ath10k_hif_ops {
+       /* Send the head of a buffer to HIF for transmission to the target. */
+       int (*send_head)(struct ath10k *ar, u8 pipe_id,
+                        unsigned int transfer_id,
+                        unsigned int nbytes,
+                        struct sk_buff *buf);
+
+       /*
+        * API to handle HIF-specific BMI message exchanges, this API is
+        * synchronous and only allowed to be called from a context that
+        * can block (sleep)
+        */
+       int (*exchange_bmi_msg)(struct ath10k *ar,
+                               void *request, u32 request_len,
+                               void *response, u32 *response_len);
+
+       int (*start)(struct ath10k *ar);
+
+       void (*stop)(struct ath10k *ar);
+
+       int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
+                                  u8 *ul_pipe, u8 *dl_pipe,
+                                  int *ul_is_polled, int *dl_is_polled);
+
+       void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
+
+       /*
+        * Check if prior sends have completed.
+        *
+        * Check whether the pipe in question has any completed
+        * sends that have not yet been processed.
+        * This function is only relevant for HIF pipes that are configured
+        * to be polled rather than interrupt-driven.
+        */
+       void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
+
+       void (*init)(struct ath10k *ar,
+                    struct ath10k_hif_cb *callbacks);
+
+       u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+};
+
+
+static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                      unsigned int transfer_id,
+                                      unsigned int nbytes,
+                                      struct sk_buff *buf)
+{
+       return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+}
+
+static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
+                                             void *request, u32 request_len,
+                                             void *response, u32 *response_len)
+{
+       return ar->hif.ops->exchange_bmi_msg(ar, request, request_len,
+                                            response, response_len);
+}
+
+static inline int ath10k_hif_start(struct ath10k *ar)
+{
+       return ar->hif.ops->start(ar);
+}
+
+static inline void ath10k_hif_stop(struct ath10k *ar)
+{
+       return ar->hif.ops->stop(ar);
+}
+
+static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
+                                                u16 service_id,
+                                                u8 *ul_pipe, u8 *dl_pipe,
+                                                int *ul_is_polled,
+                                                int *dl_is_polled)
+{
+       return ar->hif.ops->map_service_to_pipe(ar, service_id,
+                                               ul_pipe, dl_pipe,
+                                               ul_is_polled, dl_is_polled);
+}
+
+static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
+                                              u8 *ul_pipe, u8 *dl_pipe)
+{
+       ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe);
+}
+
+static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
+                                                 u8 pipe_id, int force)
+{
+       ar->hif.ops->send_complete_check(ar, pipe_id, force);
+}
+
+static inline void ath10k_hif_init(struct ath10k *ar,
+                                  struct ath10k_hif_cb *callbacks)
+{
+       ar->hif.ops->init(ar, callbacks);
+}
+
+static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
+                                                  u8 pipe_id)
+{
+       return ar->hif.ops->get_free_queue_number(ar, pipe_id);
+}
+
+#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
new file mode 100644 (file)
index 0000000..74363c9
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hif.h"
+#include "debug.h"
+
+/********/
+/* Send */
+/********/
+
+static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
+                                                 int force)
+{
+       /*
+        * Check whether HIF has any prior sends that have finished,
+        * have not had the post-processing done.
+        */
+       ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
+}
+
+static void ath10k_htc_control_tx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       kfree_skb(skb);
+}
+
+static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+       if (!skb) {
+               ath10k_warn("Unable to allocate ctrl skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
+       WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
+       return skb;
+}
+
+static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
+                                            struct sk_buff *skb)
+{
+       ath10k_skb_unmap(htc->ar->dev, skb);
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+}
+
+static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_restore_tx_skb(ep->htc, skb);
+
+       if (!ep->ep_ops.ep_tx_complete) {
+               ath10k_warn("no tx handler for eid %d\n", ep->eid);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
+}
+
+/* assumes tx_lock is held */
+static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep)
+{
+       if (!ep->tx_credit_flow_enabled)
+               return false;
+       if (ep->tx_credits >= ep->tx_credits_per_max_message)
+               return false;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
+                  ep->eid);
+       return true;
+}
+
+static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
+                                     struct sk_buff *skb)
+{
+       struct ath10k_htc_hdr *hdr;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       memset(hdr, 0, sizeof(*hdr));
+
+       hdr->eid = ep->eid;
+       hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+
+       spin_lock_bh(&ep->htc->tx_lock);
+       hdr->seq_no = ep->seq_no++;
+
+       if (ath10k_htc_ep_need_credit_update(ep))
+               hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
+
+       spin_unlock_bh(&ep->htc->tx_lock);
+}
+
+static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
+                               struct ath10k_htc_ep *ep,
+                               struct sk_buff *skb,
+                               u8 credits)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_prepare_tx_skb(ep, skb);
+
+       ret = ath10k_skb_map(htc->ar->dev, skb);
+       if (ret)
+               goto err;
+
+       ret = ath10k_hif_send_head(htc->ar,
+                                  ep->ul_pipe_id,
+                                  ep->eid,
+                                  skb->len,
+                                  skb);
+       if (unlikely(ret))
+               goto err;
+
+       return 0;
+err:
+       ath10k_warn("HTC issue failed: %d\n", ret);
+
+       spin_lock_bh(&htc->tx_lock);
+       ep->tx_credits += credits;
+       spin_unlock_bh(&htc->tx_lock);
+
+       /* this is the simplest way to handle out-of-resources for non-credit
+        * based endpoints. credit based endpoints can still get -ENOSR, but
+        * this is highly unlikely as credit reservation should prevent that */
+       if (ret == -ENOSR) {
+               spin_lock_bh(&htc->tx_lock);
+               __skb_queue_head(&ep->tx_queue, skb);
+               spin_unlock_bh(&htc->tx_lock);
+
+               return ret;
+       }
+
+       skb_cb->is_aborted = true;
+       ath10k_htc_notify_tx_completion(ep, skb);
+
+       return ret;
+}
+
+static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
+                                                      struct ath10k_htc_ep *ep,
+                                                      u8 *credits)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+       int credits_required;
+       int remainder;
+       unsigned int transfer_len;
+
+       lockdep_assert_held(&htc->tx_lock);
+
+       skb = __skb_dequeue(&ep->tx_queue);
+       if (!skb)
+               return NULL;
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       transfer_len = skb->len;
+
+       if (likely(transfer_len <= htc->target_credit_size)) {
+               credits_required = 1;
+       } else {
+               /* figure out how many credits this message requires */
+               credits_required = transfer_len / htc->target_credit_size;
+               remainder = transfer_len % htc->target_credit_size;
+
+               if (remainder)
+                       credits_required++;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
+                  credits_required, ep->tx_credits);
+
+       if (ep->tx_credits < credits_required) {
+               __skb_queue_head(&ep->tx_queue, skb);
+               return NULL;
+       }
+
+       ep->tx_credits -= credits_required;
+       *credits = credits_required;
+       return skb;
+}
+
+static void ath10k_htc_send_work(struct work_struct *work)
+{
+       struct ath10k_htc_ep *ep = container_of(work,
+                                       struct ath10k_htc_ep, send_work);
+       struct ath10k_htc *htc = ep->htc;
+       struct sk_buff *skb;
+       u8 credits = 0;
+       int ret;
+
+       while (true) {
+               if (ep->ul_is_polled)
+                       ath10k_htc_send_complete_check(ep, 0);
+
+               spin_lock_bh(&htc->tx_lock);
+               if (ep->tx_credit_flow_enabled)
+                       skb = ath10k_htc_get_skb_credit_based(htc, ep,
+                                                             &credits);
+               else
+                       skb = __skb_dequeue(&ep->tx_queue);
+               spin_unlock_bh(&htc->tx_lock);
+
+               if (!skb)
+                       break;
+
+               ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
+               if (ret == -ENOSR)
+                       break;
+       }
+}
+
+int ath10k_htc_send(struct ath10k_htc *htc,
+                   enum ath10k_htc_ep_id eid,
+                   struct sk_buff *skb)
+{
+       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+
+       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);
+       __skb_queue_tail(&ep->tx_queue, skb);
+       spin_unlock_bh(&htc->tx_lock);
+
+       queue_work(htc->ar->workqueue, &ep->send_work);
+       return 0;
+}
+
+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_ep *ep = &htc->endpoint[eid];
+       bool stopping;
+
+       ath10k_htc_notify_tx_completion(ep, skb);
+       /* the skb now belongs to the completion handler */
+
+       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
+                */
+               queue_work(ar->workqueue, &ep->send_work);
+
+       return 0;
+}
+
+/* flush endpoint TX queue */
+static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
+                                        struct ath10k_htc_ep *ep)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       spin_lock_bh(&htc->tx_lock);
+       for (;;) {
+               skb = __skb_dequeue(&ep->tx_queue);
+               if (!skb)
+                       break;
+
+               skb_cb = ATH10K_SKB_CB(skb);
+               skb_cb->is_aborted = true;
+               ath10k_htc_notify_tx_completion(ep, skb);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+
+       cancel_work_sync(&ep->send_work);
+}
+
+/***********/
+/* Receive */
+/***********/
+
+static void
+ath10k_htc_process_credit_report(struct ath10k_htc *htc,
+                                const struct ath10k_htc_credit_report *report,
+                                int len,
+                                enum ath10k_htc_ep_id eid)
+{
+       struct ath10k_htc_ep *ep;
+       int i, n_reports;
+
+       if (len % sizeof(*report))
+               ath10k_warn("Uneven credit report len %d", len);
+
+       n_reports = len / sizeof(*report);
+
+       spin_lock_bh(&htc->tx_lock);
+       for (i = 0; i < n_reports; i++, report++) {
+               if (report->eid >= ATH10K_HTC_EP_COUNT)
+                       break;
+
+               ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n",
+                          report->eid, report->credits);
+
+               ep = &htc->endpoint[report->eid];
+               ep->tx_credits += report->credits;
+
+               if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
+                       queue_work(htc->ar->workqueue, &ep->send_work);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+}
+
+static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
+                                     u8 *buffer,
+                                     int length,
+                                     enum ath10k_htc_ep_id src_eid)
+{
+       int status = 0;
+       struct ath10k_htc_record *record;
+       u8 *orig_buffer;
+       int orig_length;
+       size_t len;
+
+       orig_buffer = buffer;
+       orig_length = length;
+
+       while (length > 0) {
+               record = (struct ath10k_htc_record *)buffer;
+
+               if (length < sizeof(record->hdr)) {
+                       status = -EINVAL;
+                       break;
+               }
+
+               if (record->hdr.len > length) {
+                       /* no room left in buffer for record */
+                       ath10k_warn("Invalid record length: %d\n",
+                                   record->hdr.len);
+                       status = -EINVAL;
+                       break;
+               }
+
+               switch (record->hdr.id) {
+               case ATH10K_HTC_RECORD_CREDITS:
+                       len = sizeof(struct ath10k_htc_credit_report);
+                       if (record->hdr.len < len) {
+                               ath10k_warn("Credit report too long\n");
+                               status = -EINVAL;
+                               break;
+                       }
+                       ath10k_htc_process_credit_report(htc,
+                                                        record->credit_report,
+                                                        record->hdr.len,
+                                                        src_eid);
+                       break;
+               default:
+                       ath10k_warn("Unhandled record: id:%d length:%d\n",
+                                   record->hdr.id, record->hdr.len);
+                       break;
+               }
+
+               if (status)
+                       break;
+
+               /* multiple records may be present in a trailer */
+               buffer += sizeof(record->hdr) + record->hdr.len;
+               length -= sizeof(record->hdr) + record->hdr.len;
+       }
+
+       if (status)
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "",
+                               orig_buffer, orig_length);
+
+       return status;
+}
+
+static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           u8 pipe_id)
+{
+       int status = 0;
+       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc_hdr *hdr;
+       struct ath10k_htc_ep *ep;
+       u16 payload_len;
+       u32 trailer_len = 0;
+       size_t min_len;
+       u8 eid;
+       bool trailer_present;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       skb_pull(skb, sizeof(*hdr));
+
+       eid = hdr->eid;
+
+       if (eid >= ATH10K_HTC_EP_COUNT) {
+               ath10k_warn("HTC Rx: invalid eid %d\n", eid);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       ep = &htc->endpoint[eid];
+
+       /*
+        * If this endpoint that received a message from the target has
+        * a to-target HIF pipe whose send completions are polled rather
+        * than interrupt-driven, this is a good point to ask HIF to check
+        * whether it has any completed sends to handle.
+        */
+       if (ep->ul_is_polled)
+               ath10k_htc_send_complete_check(ep, 1);
+
+       payload_len = __le16_to_cpu(hdr->len);
+
+       if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
+               ath10k_warn("HTC rx frame too long, len: %zu\n",
+                           payload_len + sizeof(*hdr));
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       if (skb->len < payload_len) {
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC Rx: insufficient length, got %d, expected %d\n",
+                          skb->len, payload_len);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len",
+                               "", hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       /* get flags to check for trailer */
+       trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
+       if (trailer_present) {
+               u8 *trailer;
+
+               trailer_len = hdr->trailer_len;
+               min_len = sizeof(struct ath10k_ath10k_htc_record_hdr);
+
+               if ((trailer_len < min_len) ||
+                   (trailer_len > payload_len)) {
+                       ath10k_warn("Invalid trailer length: %d\n",
+                                   trailer_len);
+                       status = -EPROTO;
+                       goto out;
+               }
+
+               trailer = (u8 *)hdr;
+               trailer += sizeof(*hdr);
+               trailer += payload_len;
+               trailer -= trailer_len;
+               status = ath10k_htc_process_trailer(htc, trailer,
+                                                   trailer_len, hdr->eid);
+               if (status)
+                       goto out;
+
+               skb_trim(skb, skb->len - trailer_len);
+       }
+
+       if (((int)payload_len - (int)trailer_len) <= 0)
+               /* zero length packet with trailer data, just drop these */
+               goto out;
+
+       if (eid == ATH10K_HTC_EP_0) {
+               struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data;
+
+               switch (__le16_to_cpu(msg->hdr.message_id)) {
+               default:
+                       /* handle HTC control message */
+                       if (completion_done(&htc->ctl_resp)) {
+                               /*
+                                * this is a fatal error, target should not be
+                                * sending unsolicited messages on the ep 0
+                                */
+                               ath10k_warn("HTC rx ctrl still processing\n");
+                               status = -EINVAL;
+                               complete(&htc->ctl_resp);
+                               goto out;
+                       }
+
+                       htc->control_resp_len =
+                               min_t(int, skb->len,
+                                     ATH10K_HTC_MAX_CTRL_MSG_LEN);
+
+                       memcpy(htc->control_resp_buffer, skb->data,
+                              htc->control_resp_len);
+
+                       complete(&htc->ctl_resp);
+                       break;
+               case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE:
+                       htc->htc_ops.target_send_suspend_complete(ar);
+               }
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
+                  eid, skb);
+       ep->ep_ops.ep_rx_complete(ar, skb);
+
+       /* skb is now owned by the rx completion handler */
+       skb = NULL;
+out:
+       kfree_skb(skb);
+
+       return status;
+}
+
+static void ath10k_htc_control_rx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       /* This is unexpected. FW is not supposed to send regular rx on this
+        * endpoint. */
+       ath10k_warn("unexpected htc rx\n");
+       kfree_skb(skb);
+}
+
+/***************/
+/* Init/Deinit */
+/***************/
+
+static const char *htc_service_name(enum ath10k_htc_svc_id id)
+{
+       switch (id) {
+       case ATH10K_HTC_SVC_ID_RESERVED:
+               return "Reserved";
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+               return "Control";
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               return "WMI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+               return "DATA BE";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+               return "DATA BK";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+               return "DATA VI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+               return "DATA VO";
+       case ATH10K_HTC_SVC_ID_NMI_CONTROL:
+               return "NMI Control";
+       case ATH10K_HTC_SVC_ID_NMI_DATA:
+               return "NMI Data";
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               return "HTT Data";
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               return "RAW";
+       }
+
+       return "Unknown";
+}
+
+static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_ep *ep;
+       int i;
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ep->service_id = ATH10K_HTC_SVC_ID_UNUSED;
+               ep->max_ep_message_len = 0;
+               ep->max_tx_queue_depth = 0;
+               ep->eid = i;
+               skb_queue_head_init(&ep->tx_queue);
+               ep->htc = htc;
+               ep->tx_credit_flow_enabled = true;
+               INIT_WORK(&ep->send_work, ath10k_htc_send_work);
+       }
+}
+
+static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_svc_tx_credits *entry;
+
+       entry = &htc->service_tx_alloc[0];
+
+       /*
+        * for PCIE allocate all credists/HTC buffers to WMI.
+        * no buffers are used/required for data. data always
+        * remains on host.
+        */
+       entry++;
+       entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+       entry->credit_allocation = htc->total_transmit_credits;
+}
+
+static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
+                                          u16 service_id)
+{
+       u8 allocation = 0;
+       int i;
+
+       for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
+               if (htc->service_tx_alloc[i].service_id == service_id)
+                       allocation =
+                           htc->service_tx_alloc[i].credit_allocation;
+       }
+
+       return allocation;
+}
+
+int ath10k_htc_wait_target(struct ath10k_htc *htc)
+{
+       int status = 0;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       struct ath10k_htc_msg *msg;
+       u16 message_id;
+       u16 credit_count;
+       u16 credit_size;
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_hif_start(htc->ar);
+       if (status) {
+               ath10k_err("could not start HIF (%d)\n", status);
+               goto err_start;
+       }
+
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_WAIT_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+
+               ath10k_err("ctl_resp never came in (%d)\n", status);
+               goto err_target;
+       }
+
+       if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
+               ath10k_err("Invalid HTC ready msg len:%d\n",
+                          htc->control_resp_len);
+
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       message_id   = __le16_to_cpu(msg->hdr.message_id);
+       credit_count = __le16_to_cpu(msg->ready.credit_count);
+       credit_size  = __le16_to_cpu(msg->ready.credit_size);
+
+       if (message_id != ATH10K_HTC_MSG_READY_ID) {
+               ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       htc->total_transmit_credits = credit_count;
+       htc->target_credit_size = credit_size;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "Target ready! transmit resources: %d size:%d\n",
+                  htc->total_transmit_credits,
+                  htc->target_credit_size);
+
+       if ((htc->total_transmit_credits == 0) ||
+           (htc->target_credit_size == 0)) {
+               status = -ECOMM;
+               ath10k_err("Invalid credit size received\n");
+               goto err_target;
+       }
+
+       ath10k_htc_setup_target_buffer_assignments(htc);
+
+       /* setup our pseudo HTC control endpoint connection */
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+       conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete;
+       conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS;
+       conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL;
+
+       /* connect fake service */
+       status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
+       if (status) {
+               ath10k_err("could not connect to htc service (%d)\n", status);
+               goto err_target;
+       }
+
+       return 0;
+err_target:
+       ath10k_hif_stop(htc->ar);
+err_start:
+       return status;
+}
+
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+                              struct ath10k_htc_svc_conn_req *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp)
+{
+       struct ath10k_htc_msg *msg;
+       struct ath10k_htc_conn_svc *req_msg;
+       struct ath10k_htc_conn_svc_response resp_msg_dummy;
+       struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy;
+       enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT;
+       struct ath10k_htc_ep *ep;
+       struct sk_buff *skb;
+       unsigned int max_msg_size = 0;
+       int length, status;
+       bool disable_credit_flow_ctrl = false;
+       u16 message_id, service_id, flags = 0;
+       u8 tx_alloc = 0;
+
+       /* special case for HTC pseudo control service */
+       if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) {
+               disable_credit_flow_ctrl = true;
+               assigned_eid = ATH10K_HTC_EP_0;
+               max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN;
+               memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
+               goto setup;
+       }
+
+       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));
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb) {
+               ath10k_err("Failed to allocate HTC packet\n");
+               return -ENOMEM;
+       }
+
+       length = sizeof(msg->hdr) + sizeof(msg->connect_service);
+       skb_put(skb, length);
+       memset(skb->data, 0, length);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID);
+
+       flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
+
+       req_msg = &msg->connect_service;
+       req_msg->flags = __cpu_to_le16(flags);
+       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
+       /* Only enable credit flow control for WMI ctrl service */
+       if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
+               flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
+               disable_credit_flow_ctrl = true;
+       }
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       /* wait for response */
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+               ath10k_err("Service connect timeout: %d\n", status);
+               return status;
+       }
+
+       /* we controlled the buffer creation, it's aligned */
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       resp_msg = &msg->connect_service_response;
+       message_id = __le16_to_cpu(msg->hdr.message_id);
+       service_id = __le16_to_cpu(resp_msg->service_id);
+
+       if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
+           (htc->control_resp_len < sizeof(msg->hdr) +
+            sizeof(msg->connect_service_response))) {
+               ath10k_err("Invalid resp message ID 0x%x", message_id);
+               return -EPROTO;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
+                  htc_service_name(service_id),
+                  resp_msg->status, resp_msg->eid);
+
+       conn_resp->connect_resp_code = resp_msg->status;
+
+       /* check response status */
+       if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
+               ath10k_err("HTC Service %s connect request failed: 0x%x)\n",
+                          htc_service_name(service_id),
+                          resp_msg->status);
+               return -EPROTO;
+       }
+
+       assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid;
+       max_msg_size = __le16_to_cpu(resp_msg->max_msg_size);
+
+setup:
+
+       if (assigned_eid >= ATH10K_HTC_EP_COUNT)
+               return -EPROTO;
+
+       if (max_msg_size == 0)
+               return -EPROTO;
+
+       ep = &htc->endpoint[assigned_eid];
+       ep->eid = assigned_eid;
+
+       if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED)
+               return -EPROTO;
+
+       /* return assigned endpoint to caller */
+       conn_resp->eid = assigned_eid;
+       conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size);
+
+       /* setup the endpoint */
+       ep->service_id = conn_req->service_id;
+       ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
+       ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
+       ep->tx_credits = tx_alloc;
+       ep->tx_credit_size = htc->target_credit_size;
+       ep->tx_credits_per_max_message = ep->max_ep_message_len /
+                                        htc->target_credit_size;
+
+       if (ep->max_ep_message_len % htc->target_credit_size)
+               ep->tx_credits_per_max_message++;
+
+       /* copy all the callbacks */
+       ep->ep_ops = conn_req->ep_ops;
+
+       status = ath10k_hif_map_service_to_pipe(htc->ar,
+                                               ep->service_id,
+                                               &ep->ul_pipe_id,
+                                               &ep->dl_pipe_id,
+                                               &ep->ul_is_polled,
+                                               &ep->dl_is_polled);
+       if (status)
+               return status;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+                  htc_service_name(ep->service_id), ep->ul_pipe_id,
+                  ep->dl_pipe_id, ep->eid);
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "EP %d UL polled: %d, DL polled: %d\n",
+                  ep->eid, ep->ul_is_polled, ep->dl_is_polled);
+
+       if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
+               ep->tx_credit_flow_enabled = false;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC service: %s eid: %d TX flow control disabled\n",
+                          htc_service_name(ep->service_id), assigned_eid);
+       }
+
+       return status;
+}
+
+struct sk_buff *ath10k_htc_alloc_skb(int size)
+{
+       struct sk_buff *skb;
+
+       skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+       if (!skb) {
+               ath10k_warn("could not allocate HTC tx skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
+
+       /* FW/HTC requires 4-byte aligned streams */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned HTC tx skb\n");
+
+       return skb;
+}
+
+int ath10k_htc_start(struct ath10k_htc *htc)
+{
+       struct sk_buff *skb;
+       int status = 0;
+       struct ath10k_htc_msg *msg;
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext));
+       memset(skb->data, 0, skb->len);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       return 0;
+}
+
+/*
+ * stop HTC communications, i.e. stop interrupt reception, and flush all
+ * queued buffers
+ */
+void ath10k_htc_stop(struct ath10k_htc *htc)
+{
+       int i;
+       struct ath10k_htc_ep *ep;
+
+       spin_lock_bh(&htc->tx_lock);
+       htc->stopping = true;
+       spin_unlock_bh(&htc->tx_lock);
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ath10k_htc_flush_endpoint_tx(htc, ep);
+       }
+
+       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)
+{
+       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);
+
+       spin_lock_init(&htc->tx_lock);
+
+       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
+
+       ath10k_htc_reset_endpoint_states(htc);
+
+       /* setup HIF layer callbacks */
+       htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
+       htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
+       htc->ar = ar;
+
+       /* Get HIF default pipe for HTC message exchange */
+       ep = &htc->endpoint[ATH10K_HTC_EP_0];
+
+       ath10k_hif_init(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);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
new file mode 100644 (file)
index 0000000..fa45844
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HTC_H_
+#define _HTC_H_
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/skbuff.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+
+struct ath10k;
+
+/****************/
+/* HTC protocol */
+/****************/
+
+/*
+ * HTC - host-target control protocol
+ *
+ * tx packets are generally <htc_hdr><payload>
+ * rx packets are more complex: <htc_hdr><payload><trailer>
+ *
+ * The payload + trailer length is stored in len.
+ * To get payload-only length one needs to payload - trailer_len.
+ *
+ * Trailer contains (possibly) multiple <htc_record>.
+ * Each record is a id-len-value.
+ *
+ * HTC header flags, control_byte0, control_byte1
+ * have different meaning depending whether its tx
+ * or rx.
+ *
+ * Alignment: htc_hdr, payload and trailer are
+ * 4-byte aligned.
+ */
+
+enum ath10k_htc_tx_flags {
+       ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
+       ATH10K_HTC_FLAG_SEND_BUNDLE        = 0x02
+};
+
+enum ath10k_htc_rx_flags {
+       ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02,
+       ATH10K_HTC_FLAG_BUNDLE_MASK     = 0xF0
+};
+
+struct ath10k_htc_hdr {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */
+       __le16 len;
+       union {
+               u8 trailer_len; /* for rx */
+               u8 control_byte0;
+       } __packed;
+       union {
+               u8 seq_no; /* for tx */
+               u8 control_byte1;
+       } __packed;
+       u8 pad0;
+       u8 pad1;
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_msg_id {
+       ATH10K_HTC_MSG_READY_ID                = 1,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_ID      = 2,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_ID       = 4,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID    = 5,
+       ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE   = 6
+};
+
+enum ath10k_htc_version {
+       ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
+       ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
+};
+
+enum ath10k_htc_conn_flags {
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH    = 0x0,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF      = 0x1,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY         = 0x3,
+#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3
+       ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE    = 1 << 2,
+       ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB  8
+};
+
+enum ath10k_htc_conn_svc_status {
+       ATH10K_HTC_CONN_SVC_STATUS_SUCCESS      = 0,
+       ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND    = 1,
+       ATH10K_HTC_CONN_SVC_STATUS_FAILED       = 2,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP   = 4
+};
+
+struct ath10k_ath10k_htc_msg_hdr {
+       __le16 message_id; /* @enum htc_message_id */
+} __packed;
+
+struct ath10k_htc_unknown {
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_ready {
+       __le16 credit_count;
+       __le16 credit_size;
+       u8 max_endpoints;
+       u8 pad0;
+} __packed;
+
+struct ath10k_htc_ready_extended {
+       struct ath10k_htc_ready base;
+       u8 htc_version; /* @enum ath10k_htc_version */
+       u8 max_msgs_per_htc_bundle;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc {
+       __le16 service_id;
+       __le16 flags; /* @enum ath10k_htc_conn_flags */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc_response {
+       __le16 service_id;
+       u8 status; /* @enum ath10k_htc_conn_svc_status */
+       u8 eid;
+       __le16 max_msg_size;
+} __packed;
+
+struct ath10k_htc_setup_complete_extended {
+       u8 pad0;
+       u8 pad1;
+       __le32 flags; /* @enum htc_setup_complete_flags */
+       u8 max_msgs_per_bundled_recv;
+       u8 pad2;
+       u8 pad3;
+       u8 pad4;
+} __packed;
+
+struct ath10k_htc_msg {
+       struct ath10k_ath10k_htc_msg_hdr hdr;
+       union {
+               /* host-to-target */
+               struct ath10k_htc_conn_svc connect_service;
+               struct ath10k_htc_ready ready;
+               struct ath10k_htc_ready_extended ready_ext;
+               struct ath10k_htc_unknown unknown;
+               struct ath10k_htc_setup_complete_extended setup_complete_ext;
+
+               /* target-to-host */
+               struct ath10k_htc_conn_svc_response connect_service_response;
+       };
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_record_id {
+       ATH10K_HTC_RECORD_NULL    = 0,
+       ATH10K_HTC_RECORD_CREDITS = 1
+};
+
+struct ath10k_ath10k_htc_record_hdr {
+       u8 id; /* @enum ath10k_ath10k_htc_record_id */
+       u8 len;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_credit_report {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 credits;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_record {
+       struct ath10k_ath10k_htc_record_hdr hdr;
+       union {
+               struct ath10k_htc_credit_report credit_report[0];
+               u8 pauload[0];
+       };
+} __packed __aligned(4);
+
+/*
+ * note: the trailer offset is dynamic depending
+ * on payload length. this is only a struct layout draft
+ */
+struct ath10k_htc_frame {
+       struct ath10k_htc_hdr hdr;
+       union {
+               struct ath10k_htc_msg msg;
+               u8 payload[0];
+       };
+       struct ath10k_htc_record trailer[0];
+} __packed __aligned(4);
+
+
+/*******************/
+/* Host-side stuff */
+/*******************/
+
+enum ath10k_htc_svc_gid {
+       ATH10K_HTC_SVC_GRP_RSVD = 0,
+       ATH10K_HTC_SVC_GRP_WMI = 1,
+       ATH10K_HTC_SVC_GRP_NMI = 2,
+       ATH10K_HTC_SVC_GRP_HTT = 3,
+
+       ATH10K_HTC_SVC_GRP_TEST = 254,
+       ATH10K_HTC_SVC_GRP_LAST = 255,
+};
+
+#define SVC(group, idx) \
+       (int)(((int)(group) << 8) | (int)(idx))
+
+enum ath10k_htc_svc_id {
+       /* NOTE: service ID of 0x0000 is reserved and should never be used */
+       ATH10K_HTC_SVC_ID_RESERVED      = 0x0000,
+       ATH10K_HTC_SVC_ID_UNUSED        = ATH10K_HTC_SVC_ID_RESERVED,
+
+       ATH10K_HTC_SVC_ID_RSVD_CTRL     = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1),
+       ATH10K_HTC_SVC_ID_WMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_WMI, 0),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BE   = SVC(ATH10K_HTC_SVC_GRP_WMI, 1),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BK   = SVC(ATH10K_HTC_SVC_GRP_WMI, 2),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VI   = SVC(ATH10K_HTC_SVC_GRP_WMI, 3),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VO   = SVC(ATH10K_HTC_SVC_GRP_WMI, 4),
+
+       ATH10K_HTC_SVC_ID_NMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_NMI, 0),
+       ATH10K_HTC_SVC_ID_NMI_DATA      = SVC(ATH10K_HTC_SVC_GRP_NMI, 1),
+
+       ATH10K_HTC_SVC_ID_HTT_DATA_MSG  = SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
+
+       /* raw stream service (i.e. flash, tcmd, calibration apps) */
+       ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
+};
+
+#undef SVC
+
+enum ath10k_htc_ep_id {
+       ATH10K_HTC_EP_UNUSED = -1,
+       ATH10K_HTC_EP_0 = 0,
+       ATH10K_HTC_EP_1 = 1,
+       ATH10K_HTC_EP_2,
+       ATH10K_HTC_EP_3,
+       ATH10K_HTC_EP_4,
+       ATH10K_HTC_EP_5,
+       ATH10K_HTC_EP_6,
+       ATH10K_HTC_EP_7,
+       ATH10K_HTC_EP_8,
+       ATH10K_HTC_EP_COUNT,
+};
+
+struct ath10k_htc_ops {
+       void (*target_send_suspend_complete)(struct ath10k *ar);
+};
+
+struct ath10k_htc_ep_ops {
+       void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
+       void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+};
+
+/* service connection information */
+struct ath10k_htc_svc_conn_req {
+       u16 service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+       int max_send_queue_depth;
+};
+
+/* service connection response information */
+struct ath10k_htc_svc_conn_resp {
+       u8 buffer_len;
+       u8 actual_len;
+       enum ath10k_htc_ep_id eid;
+       unsigned int max_msg_len;
+       u8 connect_resp_code;
+};
+
+#define ATH10K_NUM_CONTROL_TX_BUFFERS 2
+#define ATH10K_HTC_MAX_LEN 4096
+#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256
+#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ)
+#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \
+                                       sizeof(struct ath10k_htc_hdr))
+#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ)
+
+struct ath10k_htc_ep {
+       struct ath10k_htc *htc;
+       enum ath10k_htc_ep_id eid;
+       enum ath10k_htc_svc_id service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+
+       int max_tx_queue_depth;
+       int max_ep_message_len;
+       u8 ul_pipe_id;
+       u8 dl_pipe_id;
+       int ul_is_polled; /* call HIF to get tx completions */
+       int dl_is_polled; /* call HIF to fetch rx (not implemented) */
+
+       struct sk_buff_head tx_queue;
+
+       u8 seq_no; /* for debugging */
+       int tx_credits;
+       int tx_credit_size;
+       int tx_credits_per_max_message;
+       bool tx_credit_flow_enabled;
+
+       struct work_struct send_work;
+};
+
+struct ath10k_htc_svc_tx_credits {
+       u16 service_id;
+       u8  credit_allocation;
+};
+
+struct ath10k_htc {
+       struct ath10k *ar;
+       struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
+
+       /* protects endpoint and stopping fields */
+       spinlock_t tx_lock;
+
+       struct ath10k_htc_ops htc_ops;
+
+       u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN];
+       int control_resp_len;
+
+       struct completion ctl_resp;
+
+       int total_transmit_credits;
+       struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
+       int target_credit_size;
+
+       bool stopping;
+};
+
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+                                    struct ath10k_htc_ops *htc_ops);
+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,
+                              struct ath10k_htc_svc_conn_req  *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp);
+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
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
new file mode 100644 (file)
index 0000000..185a546
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+
+#include "htt.h"
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+{
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       int status;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler;
+
+       /* 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,
+                                           &conn_resp);
+
+       if (status)
+               return status;
+
+       htt->eid = conn_resp.eid;
+
+       return 0;
+}
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+{
+       struct ath10k_htt *htt;
+       int ret;
+
+       htt = kzalloc(sizeof(*htt), GFP_KERNEL);
+       if (!htt)
+               return NULL;
+
+       htt->ar = ar;
+       htt->max_throughput_mbps = 800;
+
+       /*
+        * Connect to HTC service.
+        * This has to be done before calling ath10k_htt_rx_attach,
+        * since ath10k_htt_rx_attach involves sending a rx ring configure
+        * message to the target.
+        */
+       if (ath10k_htt_htc_attach(htt))
+               goto err_htc_attach;
+
+       ret = ath10k_htt_tx_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt tx (%d)\n", ret);
+               goto err_htc_attach;
+       }
+
+       if (ath10k_htt_rx_attach(htt))
+               goto err_rx_attach;
+
+       /*
+        * Prefetch enough data to satisfy target
+        * classification engine.
+        * This is for LL chips. HL chips will probably
+        * transfer all frame in the tx fragment.
+        */
+       htt->prefetch_len =
+               36 + /* 802.11 + qos + ht */
+               4 + /* 802.1q */
+               8 + /* llc snap */
+               2; /* ip4 dscp or ip6 priority */
+
+       return htt;
+
+err_rx_attach:
+       ath10k_htt_tx_detach(htt);
+err_htc_attach:
+       kfree(htt);
+       return NULL;
+}
+
+#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
+
+static int ath10k_htt_verify_version(struct ath10k_htt *htt)
+{
+       ath10k_dbg(ATH10K_DBG_HTT,
+                  "htt target version %d.%d; host version %d.%d\n",
+                   htt->target_version_major,
+                   htt->target_version_minor,
+                   HTT_CURRENT_VERSION_MAJOR,
+                   HTT_CURRENT_VERSION_MINOR);
+
+       if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
+               ath10k_err("htt major versions are incompatible!\n");
+               return -ENOTSUPP;
+       }
+
+       if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
+               ath10k_warn("htt minor version differ but still compatible\n");
+
+       return 0;
+}
+
+int ath10k_htt_attach_target(struct ath10k_htt *htt)
+{
+       int status;
+
+       init_completion(&htt->target_version_received);
+
+       status = ath10k_htt_h2t_ver_req_msg(htt);
+       if (status)
+               return status;
+
+       status = wait_for_completion_timeout(&htt->target_version_received,
+                                               HTT_TARGET_VERSION_TIMEOUT_HZ);
+       if (status <= 0) {
+               ath10k_warn("htt version request timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       status = ath10k_htt_verify_version(htt);
+       if (status)
+               return status;
+
+       return ath10k_htt_send_rx_ring_cfg_ll(htt);
+}
+
+void ath10k_htt_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_rx_detach(htt);
+       ath10k_htt_tx_detach(htt);
+       kfree(htt);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
new file mode 100644 (file)
index 0000000..a7a7aa0
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HTT_H_
+#define _HTT_H_
+
+#include <linux/bug.h>
+
+#include "core.h"
+#include "htc.h"
+#include "rx_desc.h"
+
+#define HTT_CURRENT_VERSION_MAJOR      2
+#define HTT_CURRENT_VERSION_MINOR      1
+
+enum htt_dbg_stats_type {
+       HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
+       HTT_DBG_STATS_RX_REORDER    = 1 << 1,
+       HTT_DBG_STATS_RX_RATE_INFO  = 1 << 2,
+       HTT_DBG_STATS_TX_PPDU_LOG   = 1 << 3,
+       HTT_DBG_STATS_TX_RATE_INFO  = 1 << 4,
+       /* bits 5-23 currently reserved */
+
+       HTT_DBG_NUM_STATS /* keep this last */
+};
+
+enum htt_h2t_msg_type { /* host-to-target */
+       HTT_H2T_MSG_TYPE_VERSION_REQ        = 0,
+       HTT_H2T_MSG_TYPE_TX_FRM             = 1,
+       HTT_H2T_MSG_TYPE_RX_RING_CFG        = 2,
+       HTT_H2T_MSG_TYPE_STATS_REQ          = 3,
+       HTT_H2T_MSG_TYPE_SYNC               = 4,
+       HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
+       HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+       HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
+
+       HTT_H2T_NUM_MSGS /* keep this last */
+};
+
+struct htt_cmd_hdr {
+       u8 msg_type;
+} __packed;
+
+struct htt_ver_req {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+} __packed;
+
+/*
+ * HTT tx MSDU descriptor
+ *
+ * The HTT tx MSDU descriptor is created by the host HTT SW for each
+ * tx MSDU.  The HTT tx MSDU descriptor contains the information that
+ * the target firmware needs for the FW's tx processing, particularly
+ * for creating the HW msdu descriptor.
+ * The same HTT tx descriptor is used for HL and LL systems, though
+ * a few fields within the tx descriptor are used only by LL or
+ * only by HL.
+ * The HTT tx descriptor is defined in two manners: by a struct with
+ * bitfields, and by a series of [dword offset, bit mask, bit shift]
+ * definitions.
+ * The target should use the struct def, for simplicitly and clarity,
+ * but the host shall use the bit-mast + bit-shift defs, to be endian-
+ * neutral.  Specifically, the host shall use the get/set macros built
+ * around the mask + shift defs.
+ */
+struct htt_data_tx_desc_frag {
+       __le32 paddr;
+       __le32 len;
+} __packed;
+
+enum htt_data_tx_desc_flags0 {
+       HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
+       HTT_DATA_TX_DESC_FLAGS0_NO_AGGR         = 1 << 1,
+       HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT      = 1 << 2,
+       HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY     = 1 << 3,
+       HTT_DATA_TX_DESC_FLAGS0_RSVD0           = 1 << 4
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5
+};
+
+enum htt_data_tx_desc_flags1 {
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB  0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB  6
+       HTT_DATA_TX_DESC_FLAGS1_POSTPONED        = 1 << 11,
+       HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH    = 1 << 12,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14,
+       HTT_DATA_TX_DESC_FLAGS1_RSVD1            = 1 << 15
+};
+
+enum htt_data_tx_ext_tid {
+       HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16,
+       HTT_DATA_TX_EXT_TID_MGMT                = 17,
+       HTT_DATA_TX_EXT_TID_INVALID             = 31
+};
+
+#define HTT_INVALID_PEERID 0xFFFF
+
+/*
+ * htt_data_tx_desc - used for data tx path
+ *
+ * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1.
+ *       ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_
+ *                for special kinds of tids
+ *       postponed: only for HL hosts. indicates if this is a resend
+ *                  (HL hosts manage queues on the host )
+ *       more_in_batch: only for HL hosts. indicates if more packets are
+ *                      pending. this allows target to wait and aggregate
+ */
+struct htt_data_tx_desc {
+       u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
+       __le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */
+       __le16 len;
+       __le16 id;
+       __le32 frags_paddr;
+       __le32 peerid;
+       u8 prefetch[0]; /* start of frame, for FW classification engine */
+} __packed;
+
+enum htt_rx_ring_flags {
+       HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0,
+       HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1,
+       HTT_RX_RING_FLAGS_PPDU_START   = 1 << 2,
+       HTT_RX_RING_FLAGS_PPDU_END     = 1 << 3,
+       HTT_RX_RING_FLAGS_MPDU_START   = 1 << 4,
+       HTT_RX_RING_FLAGS_MPDU_END     = 1 << 5,
+       HTT_RX_RING_FLAGS_MSDU_START   = 1 << 6,
+       HTT_RX_RING_FLAGS_MSDU_END     = 1 << 7,
+       HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8,
+       HTT_RX_RING_FLAGS_FRAG_INFO    = 1 << 9,
+       HTT_RX_RING_FLAGS_UNICAST_RX   = 1 << 10,
+       HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11,
+       HTT_RX_RING_FLAGS_CTRL_RX      = 1 << 12,
+       HTT_RX_RING_FLAGS_MGMT_RX      = 1 << 13,
+       HTT_RX_RING_FLAGS_NULL_RX      = 1 << 14,
+       HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
+};
+
+struct htt_rx_ring_setup_ring {
+       __le32 fw_idx_shadow_reg_paddr;
+       __le32 rx_ring_base_paddr;
+       __le16 rx_ring_len; /* in 4-byte words */
+       __le16 rx_ring_bufsize; /* rx skb size - in bytes */
+       __le16 flags; /* %HTT_RX_RING_FLAGS_ */
+       __le16 fw_idx_init_val;
+
+       /* the following offsets are in 4-byte units */
+       __le16 mac80211_hdr_offset;
+       __le16 msdu_payload_offset;
+       __le16 ppdu_start_offset;
+       __le16 ppdu_end_offset;
+       __le16 mpdu_start_offset;
+       __le16 mpdu_end_offset;
+       __le16 msdu_start_offset;
+       __le16 msdu_end_offset;
+       __le16 rx_attention_offset;
+       __le16 frag_info_offset;
+} __packed;
+
+struct htt_rx_ring_setup_hdr {
+       u8 num_rings; /* supported values: 1, 2 */
+       __le16 rsvd0;
+} __packed;
+
+struct htt_rx_ring_setup {
+       struct htt_rx_ring_setup_hdr hdr;
+       struct htt_rx_ring_setup_ring rings[0];
+} __packed;
+
+/*
+ * htt_stats_req - request target to send specified statistics
+ *
+ * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ
+ * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @cfg_val: stat_type specific configuration
+ * @stat_type: see %htt_dbg_stats_type
+ * @cookie_lsb: used for confirmation message from target->host
+ * @cookie_msb: ditto as %cookie
+ */
+struct htt_stats_req {
+       u8 upload_types[3];
+       u8 rsvd0;
+       u8 reset_types[3];
+       struct {
+               u8 mpdu_bytes;
+               u8 mpdu_num_msdus;
+               u8 msdu_bytes;
+       } __packed;
+       u8 stat_type;
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+} __packed;
+
+#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff
+
+/*
+ * htt_oob_sync_req - request out-of-band sync
+ *
+ * The HTT SYNC tells the target to suspend processing of subsequent
+ * HTT host-to-target messages until some other target agent locally
+ * informs the target HTT FW that the current sync counter is equal to
+ * or greater than (in a modulo sense) the sync counter specified in
+ * the SYNC message.
+ *
+ * This allows other host-target components to synchronize their operation
+ * with HTT, e.g. to ensure that tx frames don't get transmitted until a
+ * security key has been downloaded to and activated by the target.
+ * In the absence of any explicit synchronization counter value
+ * specification, the target HTT FW will use zero as the default current
+ * sync value.
+ *
+ * The HTT target FW will suspend its host->target message processing as long
+ * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128.
+ */
+struct htt_oob_sync_req {
+       u8 sync_count;
+       __le16 rsvd0;
+} __packed;
+
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
+
+struct htt_aggr_conf {
+       u8 max_num_ampdu_subframes;
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
+               u8 max_num_amsdu_subframes:5;
+       } __packed;
+} __packed;
+
+#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
+
+struct htt_mgmt_tx_desc {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+       __le32 msdu_paddr;
+       __le32 desc_id;
+       __le32 len;
+       __le32 vdev_id;
+       u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN];
+} __packed;
+
+enum htt_mgmt_tx_status {
+       HTT_MGMT_TX_STATUS_OK    = 0,
+       HTT_MGMT_TX_STATUS_RETRY = 1,
+       HTT_MGMT_TX_STATUS_DROP  = 2
+};
+
+/*=== target -> host messages ===============================================*/
+
+
+enum htt_t2h_msg_type {
+       HTT_T2H_MSG_TYPE_VERSION_CONF           = 0x0,
+       HTT_T2H_MSG_TYPE_RX_IND                 = 0x1,
+       HTT_T2H_MSG_TYPE_RX_FLUSH               = 0x2,
+       HTT_T2H_MSG_TYPE_PEER_MAP               = 0x3,
+       HTT_T2H_MSG_TYPE_PEER_UNMAP             = 0x4,
+       HTT_T2H_MSG_TYPE_RX_ADDBA               = 0x5,
+       HTT_T2H_MSG_TYPE_RX_DELBA               = 0x6,
+       HTT_T2H_MSG_TYPE_TX_COMPL_IND           = 0x7,
+       HTT_T2H_MSG_TYPE_PKTLOG                 = 0x8,
+       HTT_T2H_MSG_TYPE_STATS_CONF             = 0x9,
+       HTT_T2H_MSG_TYPE_RX_FRAG_IND            = 0xa,
+       HTT_T2H_MSG_TYPE_SEC_IND                = 0xb,
+       HTT_T2H_MSG_TYPE_RC_UPDATE_IND          = 0xc,
+       HTT_T2H_MSG_TYPE_TX_INSPECT_IND         = 0xd,
+       HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION     = 0xe,
+       HTT_T2H_MSG_TYPE_TEST,
+       /* keep this last */
+       HTT_T2H_NUM_MSGS
+};
+
+/*
+ * htt_resp_hdr - header for target-to-host messages
+ *
+ * msg_type: see htt_t2h_msg_type
+ */
+struct htt_resp_hdr {
+       u8 msg_type;
+} __packed;
+
+#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0
+#define HTT_RESP_HDR_MSG_TYPE_MASK   0xff
+#define HTT_RESP_HDR_MSG_TYPE_LSB    0
+
+/* htt_ver_resp - response sent for htt_ver_req */
+struct htt_ver_resp {
+       u8 minor;
+       u8 major;
+       u8 rsvd0;
+} __packed;
+
+struct htt_mgmt_tx_completion {
+       u8 rsvd0;
+       u8 rsvd1;
+       u8 rsvd2;
+       __le32 desc_id;
+       __le32 status;
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK  (0x3F)
+#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB   (0)
+#define HTT_RX_INDICATION_INFO0_FLUSH_VALID   (1 << 6)
+#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK   0x0000003F
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB    0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK     0x00000FC0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB      6
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB  12
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK   0x00FC0000
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB    18
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK     0xFF000000
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB      24
+
+struct htt_rx_indication_hdr {
+       u8 info0; /* %HTT_RX_INDICATION_INFO0_ */
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_INDICATION_INFO1_ */
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID    (1 << 0)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB  (1)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK  (1 << 5)
+#define HTT_RX_INDICATION_INFO0_END_VALID        (1 << 6)
+#define HTT_RX_INDICATION_INFO0_START_VALID      (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK    0x00FFFFFF
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB     0
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB  24
+
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB  0
+#define HTT_RX_INDICATION_INFO2_SERVICE_MASK    0xFF000000
+#define HTT_RX_INDICATION_INFO2_SERVICE_LSB     24
+
+enum htt_rx_legacy_rate {
+       HTT_RX_OFDM_48 = 0,
+       HTT_RX_OFDM_24 = 1,
+       HTT_RX_OFDM_12,
+       HTT_RX_OFDM_6,
+       HTT_RX_OFDM_54,
+       HTT_RX_OFDM_36,
+       HTT_RX_OFDM_18,
+       HTT_RX_OFDM_9,
+
+       /* long preamble */
+       HTT_RX_CCK_11_LP = 0,
+       HTT_RX_CCK_5_5_LP = 1,
+       HTT_RX_CCK_2_LP,
+       HTT_RX_CCK_1_LP,
+       /* short preamble */
+       HTT_RX_CCK_11_SP,
+       HTT_RX_CCK_5_5_SP,
+       HTT_RX_CCK_2_SP
+};
+
+enum htt_rx_legacy_rate_type {
+       HTT_RX_LEGACY_RATE_OFDM = 0,
+       HTT_RX_LEGACY_RATE_CCK
+};
+
+enum htt_rx_preamble_type {
+       HTT_RX_LEGACY        = 0x4,
+       HTT_RX_HT            = 0x8,
+       HTT_RX_HT_WITH_TXBF  = 0x9,
+       HTT_RX_VHT           = 0xC,
+       HTT_RX_VHT_WITH_TXBF = 0xD,
+};
+
+/*
+ * Fields: phy_err_valid, phy_err_code, tsf,
+ * usec_timestamp, sub_usec_timestamp
+ * ..are valid only if end_valid == 1.
+ *
+ * Fields: rssi_chains, legacy_rate_type,
+ * legacy_rate_cck, preamble_type, service,
+ * vht_sig_*
+ * ..are valid only if start_valid == 1;
+ */
+struct htt_rx_indication_ppdu {
+       u8 combined_rssi;
+       u8 sub_usec_timestamp;
+       u8 phy_err_code;
+       u8 info0; /* HTT_RX_INDICATION_INFO0_ */
+       struct {
+               u8 pri20_db;
+               u8 ext20_db;
+               u8 ext40_db;
+               u8 ext80_db;
+       } __packed rssi_chains[4];
+       __le32 tsf;
+       __le32 usec_timestamp;
+       __le32 info1; /* HTT_RX_INDICATION_INFO1_ */
+       __le32 info2; /* HTT_RX_INDICATION_INFO2_ */
+} __packed;
+
+enum htt_rx_mpdu_status {
+       HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0,
+       HTT_RX_IND_MPDU_STATUS_OK,
+       HTT_RX_IND_MPDU_STATUS_ERR_FCS,
+       HTT_RX_IND_MPDU_STATUS_ERR_DUP,
+       HTT_RX_IND_MPDU_STATUS_ERR_REPLAY,
+       HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER,
+       /* only accept EAPOL frames */
+       HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER,
+       HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC,
+       /* Non-data in promiscous mode */
+       HTT_RX_IND_MPDU_STATUS_MGMT_CTRL,
+       HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR,
+       HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR,
+       HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR,
+       HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR,
+       HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR,
+
+       /*
+        * MISC: discard for unspecified reasons.
+        * Leave this enum value last.
+        */
+       HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF
+};
+
+struct htt_rx_indication_mpdu_range {
+       u8 mpdu_count;
+       u8 mpdu_range_status; /* %htt_rx_mpdu_status */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct htt_rx_indication_prefix {
+       __le16 fw_rx_desc_bytes;
+       u8 pad0;
+       u8 pad1;
+};
+
+struct htt_rx_indication {
+       struct htt_rx_indication_hdr hdr;
+       struct htt_rx_indication_ppdu ppdu;
+       struct htt_rx_indication_prefix prefix;
+
+       /*
+        * the following fields are both dynamically sized, so
+        * take care addressing them
+        */
+
+       /* the size of this is %fw_rx_desc_bytes */
+       struct fw_rx_desc_base fw_desc;
+
+       /*
+        * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4)
+        * and has %num_mpdu_ranges elements.
+        */
+       struct htt_rx_indication_mpdu_range mpdu_ranges[0];
+} __packed;
+
+static inline struct htt_rx_indication_mpdu_range *
+               htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind)
+{
+       void *ptr = rx_ind;
+
+       ptr += sizeof(rx_ind->hdr)
+            + sizeof(rx_ind->ppdu)
+            + sizeof(rx_ind->prefix)
+            + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4);
+       return ptr;
+}
+
+enum htt_rx_flush_mpdu_status {
+       HTT_RX_FLUSH_MPDU_DISCARD = 0,
+       HTT_RX_FLUSH_MPDU_REORDER = 1,
+};
+
+/*
+ * htt_rx_flush - discard or reorder given range of mpdus
+ *
+ * Note: host must check if all sequence numbers between
+ *     [seq_num_start, seq_num_end-1] are valid.
+ */
+struct htt_rx_flush {
+       __le16 peer_id;
+       u8 tid;
+       u8 rsvd0;
+       u8 mpdu_status; /* %htt_rx_flush_mpdu_status */
+       u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */
+       u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */
+};
+
+struct htt_rx_peer_map {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 rsvd0;
+       u8 rsvd1;
+} __packed;
+
+struct htt_rx_peer_unmap {
+       u8 rsvd0;
+       __le16 peer_id;
+} __packed;
+
+enum htt_security_types {
+       HTT_SECURITY_NONE,
+       HTT_SECURITY_WEP128,
+       HTT_SECURITY_WEP104,
+       HTT_SECURITY_WEP40,
+       HTT_SECURITY_TKIP,
+       HTT_SECURITY_TKIP_NOMIC,
+       HTT_SECURITY_AES_CCMP,
+       HTT_SECURITY_WAPI,
+
+       HTT_NUM_SECURITY_TYPES /* keep this last! */
+};
+
+enum htt_security_flags {
+#define HTT_SECURITY_TYPE_MASK 0x7F
+#define HTT_SECURITY_TYPE_LSB  0
+       HTT_SECURITY_IS_UNICAST = 1 << 7
+};
+
+struct htt_security_indication {
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* %htt_security_flags */
+               struct {
+                       u8 security_type:7, /* %htt_security_types */
+                          is_unicast:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       u8 michael_key[8];
+       u8 wapi_rsc[16];
+} __packed;
+
+#define HTT_RX_BA_INFO0_TID_MASK     0x000F
+#define HTT_RX_BA_INFO0_TID_LSB      0
+#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0
+#define HTT_RX_BA_INFO0_PEER_ID_LSB  4
+
+struct htt_rx_addba {
+       u8 window_size;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+struct htt_rx_delba {
+       u8 rsvd0;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+enum htt_data_tx_status {
+       HTT_DATA_TX_STATUS_OK            = 0,
+       HTT_DATA_TX_STATUS_DISCARD       = 1,
+       HTT_DATA_TX_STATUS_NO_ACK        = 2,
+       HTT_DATA_TX_STATUS_POSTPONE      = 3, /* HL only */
+       HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128
+};
+
+enum htt_data_tx_flags {
+#define HTT_DATA_TX_STATUS_MASK 0x07
+#define HTT_DATA_TX_STATUS_LSB  0
+#define HTT_DATA_TX_TID_MASK    0x78
+#define HTT_DATA_TX_TID_LSB     3
+       HTT_DATA_TX_TID_INVALID = 1 << 7
+};
+
+#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF
+
+struct htt_data_tx_completion {
+       union {
+               u8 flags;
+               struct {
+                       u8 status:3,
+                          tid:4,
+                          tid_invalid:1;
+               } __packed;
+       } __packed;
+       u8 num_msdus;
+       u8 rsvd0;
+       __le16 msdus[0]; /* variable length based on %num_msdus */
+} __packed;
+
+struct htt_tx_compl_ind_base {
+       u32 hdr;
+       u16 payload[1/*or more*/];
+} __packed;
+
+struct htt_rc_tx_done_params {
+       u32 rate_code;
+       u32 rate_code_flags;
+       u32 flags;
+       u32 num_enqued; /* 1 for non-AMPDU */
+       u32 num_retries;
+       u32 num_failed; /* for AMPDU */
+       u32 ack_rssi;
+       u32 time_stamp;
+       u32 is_probe;
+};
+
+struct htt_rc_update {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 num_elems;
+       u8 rsvd0;
+       struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */
+} __packed;
+
+/* see htt_rx_indication for similar fields and descriptions */
+struct htt_rx_fragment_indication {
+       union {
+               u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */
+               struct {
+                       u8 ext_tid:5,
+                          flush_valid:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */
+       __le16 fw_rx_desc_bytes;
+       __le16 rsvd0;
+
+       u8 fw_msdu_rx_desc[0];
+} __packed;
+
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK     0x1F
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB      0
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB  5
+
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB  0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK   0x00000FC0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB    6
+
+/*
+ * target -> host test message definition
+ *
+ * The following field definitions describe the format of the test
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header, followed by a variable
+ * number of 32-bit integer values, followed by a variable number
+ * of 8-bit character values.
+ *
+ * |31                         16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |          num chars          |   num ints   |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                           int 0                           |
+ * |-----------------------------------------------------------|
+ * |                           int 1                           |
+ * |-----------------------------------------------------------|
+ * |                            ...                            |
+ * |-----------------------------------------------------------|
+ * |    char 3    |    char 2    |    char 1    |    char 0    |
+ * |-----------------------------------------------------------|
+ * |              |              |      ...     |    char 4    |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_TEST
+ *   - NUM_INTS
+ *     Bits 15:8
+ *     Purpose: indicate how many 32-bit integers follow the message header
+ *   - NUM_CHARS
+ *     Bits 31:16
+ *     Purpose: indicate how many 8-bit charaters follow the series of integers
+ */
+struct htt_rx_test {
+       u8 num_ints;
+       __le16 num_chars;
+
+       /* payload consists of 2 lists:
+        *  a) num_ints * sizeof(__le32)
+        *  b) num_chars * sizeof(u8) aligned to 4bytes */
+       u8 payload[0];
+} __packed;
+
+static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test)
+{
+       return (__le32 *)rx_test->payload;
+}
+
+static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test)
+{
+       return rx_test->payload + (rx_test->num_ints * sizeof(__le32));
+}
+
+/*
+ * target -> host packet log message
+ *
+ * The following field definitions describe the format of the packet log
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header,followed by a variable number
+ * of 32-bit character values.
+ *
+ * |31          24|23          16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |              |              |              |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                        payload                            |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_PACKETLOG
+ */
+struct htt_pktlog_msg {
+       u8 pad[3];
+       __le32 payload[1 /* or more */];
+} __packed;
+
+struct htt_dbg_stats_rx_reorder_stats {
+       /* Non QoS MPDUs received */
+       __le32 deliver_non_qos;
+
+       /* MPDUs received in-order */
+       __le32 deliver_in_order;
+
+       /* Flush due to reorder timer expired */
+       __le32 deliver_flush_timeout;
+
+       /* Flush due to move out of window */
+       __le32 deliver_flush_oow;
+
+       /* Flush due to DELBA */
+       __le32 deliver_flush_delba;
+
+       /* MPDUs dropped due to FCS error */
+       __le32 fcs_error;
+
+       /* MPDUs dropped due to monitor mode non-data packet */
+       __le32 mgmt_ctrl;
+
+       /* MPDUs dropped due to invalid peer */
+       __le32 invalid_peer;
+
+       /* MPDUs dropped due to duplication (non aggregation) */
+       __le32 dup_non_aggr;
+
+       /* MPDUs dropped due to processed before */
+       __le32 dup_past;
+
+       /* MPDUs dropped due to duplicate in reorder queue */
+       __le32 dup_in_reorder;
+
+       /* Reorder timeout happened */
+       __le32 reorder_timeout;
+
+       /* invalid bar ssn */
+       __le32 invalid_bar_ssn;
+
+       /* reorder reset due to bar ssn */
+       __le32 ssn_reset;
+};
+
+struct htt_dbg_stats_wal_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct htt_dbg_stats_wal_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct htt_dbg_stats_wal_peer_stats {
+       __le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+} __packed;
+
+struct htt_dbg_stats_wal_pdev_txrx {
+       struct htt_dbg_stats_wal_tx_stats tx_stats;
+       struct htt_dbg_stats_wal_rx_stats rx_stats;
+       struct htt_dbg_stats_wal_peer_stats peer_stats;
+} __packed;
+
+struct htt_dbg_stats_rx_rate_info {
+       __le32 mcs[10];
+       __le32 sgi[10];
+       __le32 nss[4];
+       __le32 stbc[10];
+       __le32 bw[3];
+       __le32 pream[6];
+       __le32 ldpc;
+       __le32 txbf;
+};
+
+/*
+ * htt_dbg_stats_status -
+ * present -     The requested stats have been delivered in full.
+ *               This indicates that either the stats information was contained
+ *               in its entirety within this message, or else this message
+ *               completes the delivery of the requested stats info that was
+ *               partially delivered through earlier STATS_CONF messages.
+ * partial -     The requested stats have been delivered in part.
+ *               One or more subsequent STATS_CONF messages with the same
+ *               cookie value will be sent to deliver the remainder of the
+ *               information.
+ * error -       The requested stats could not be delivered, for example due
+ *               to a shortage of memory to construct a message holding the
+ *               requested stats.
+ * invalid -     The requested stat type is either not recognized, or the
+ *               target is configured to not gather the stats type in question.
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * series_done - This special value indicates that no further stats info
+ *               elements are present within a series of stats info elems
+ *               (within a stats upload confirmation message).
+ */
+enum htt_dbg_stats_status {
+       HTT_DBG_STATS_STATUS_PRESENT     = 0,
+       HTT_DBG_STATS_STATUS_PARTIAL     = 1,
+       HTT_DBG_STATS_STATUS_ERROR       = 2,
+       HTT_DBG_STATS_STATUS_INVALID     = 3,
+       HTT_DBG_STATS_STATUS_SERIES_DONE = 7
+};
+
+/*
+ * target -> host statistics upload
+ *
+ * The following field definitions describe the format of the HTT target
+ * to host stats upload confirmation message.
+ * The message contains a cookie echoed from the HTT host->target stats
+ * upload request, which identifies which request the confirmation is
+ * for, and a series of tag-length-value stats information elements.
+ * The tag-length header for each stats info element also includes a
+ * status field, to indicate whether the request for the stat type in
+ * question was fully met, partially met, unable to be met, or invalid
+ * (if the stat type in question is disabled in the target).
+ * A special value of all 1's in this status field is used to indicate
+ * the end of the series of stats info elements.
+ *
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * |                  reserved                  |    msg type   |
+ * |------------------------------------------------------------|
+ * |                        cookie LSBs                         |
+ * |------------------------------------------------------------|
+ * |                        cookie MSBs                         |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |              n/a            |   reserved   | 111 |   n/a   |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Purpose: identifies this is a statistics upload confirmation message
+ *    Value: 0x9
+ *  - COOKIE_LSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: LSBs of the opaque cookie specified by the host-side requestor
+ *  - COOKIE_MSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: MSBs of the opaque cookie specified by the host-side requestor
+ *
+ * Stats Information Element tag-length header fields:
+ *  - STAT_TYPE
+ *    Bits 4:0
+ *    Purpose: identifies the type of statistics info held in the
+ *        following information element
+ *    Value: htt_dbg_stats_type
+ *  - STATUS
+ *    Bits 7:5
+ *    Purpose: indicate whether the requested stats are present
+ *    Value: htt_dbg_stats_status, including a special value (0x7) to mark
+ *        the completion of the stats entry series
+ *  - LENGTH
+ *    Bits 31:16
+ *    Purpose: indicate the stats information size
+ *    Value: This field specifies the number of bytes of stats information
+ *       that follows the element tag-length header.
+ *       It is expected but not required that this length is a multiple of
+ *       4 bytes.  Even if the length is not an integer multiple of 4, the
+ *       subsequent stats entry header will begin on a 4-byte aligned
+ *       boundary.
+ */
+
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB  0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK    0xE0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB     5
+
+struct htt_stats_conf_item {
+       union {
+               u8 info;
+               struct {
+                       u8 stat_type:5; /* %HTT_DBG_STATS_ */
+                       u8 status:3; /* %HTT_DBG_STATS_STATUS_ */
+               } __packed;
+       } __packed;
+       u8 pad;
+       __le16 length;
+       u8 payload[0]; /* roundup(length, 4) long */
+} __packed;
+
+struct htt_stats_conf {
+       u8 pad[3];
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+
+       /* each item has variable length! */
+       struct htt_stats_conf_item items[0];
+} __packed;
+
+static inline struct htt_stats_conf_item *htt_stats_conf_next_item(
+                                       const struct htt_stats_conf_item *item)
+{
+       return (void *)item + sizeof(*item) + roundup(item->length, 4);
+}
+/*
+ * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank
+ *
+ * The following field definitions describe the format of the HTT host
+ * to target frag_desc/msdu_ext bank configuration message.
+ * The message contains the based address and the min and max id of the
+ * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and
+ * MSDU_EXT/FRAG_DESC.
+ * HTT will use id in HTT descriptor instead sending the frag_desc_ptr.
+ * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0
+ * the hardware does the mapping/translation.
+ *
+ * Total banks that can be configured is configured to 16.
+ *
+ * This should be called before any TX has be initiated by the HTT
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * | DESC_SIZE    |  NUM_BANKS   | RES |SWP|pdev|    msg type   |
+ * |------------------------------------------------------------|
+ * |                     BANK0_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |                    BANK15_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |       BANK0_MAX_ID          |       BANK0_MIN_ID           |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |       BANK15_MAX_ID         |       BANK15_MIN_ID          |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Value: 0x6
+ *  - BANKx_BASE_ADDRESS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to specify the base address of the MSDU_EXT
+ *         bank physical/bus address.
+ *  - BANKx_MIN_ID
+ *    Bits 15:0
+ *    Purpose: Provide a mechanism to specify the min index that needs to
+ *          mapped.
+ *  - BANKx_MAX_ID
+ *    Bits 31:16
+ *    Purpose: Provide a mechanism to specify the max index that needs to
+ *
+ */
+struct htt_frag_desc_bank_id {
+       __le16 bank_min_id;
+       __le16 bank_max_id;
+} __packed;
+
+/* real is 16 but it wouldn't fit in the max htt message size
+ * so we use a conservatively safe value for now */
+#define HTT_FRAG_DESC_BANK_MAX 4
+
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB  0
+#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP         (1 << 2)
+
+struct htt_frag_desc_bank_cfg {
+       u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */
+       u8 num_banks;
+       u8 desc_size;
+       __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX];
+       struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX];
+} __packed;
+
+union htt_rx_pn_t {
+       /* WEP: 24-bit PN */
+       u32 pn24;
+
+       /* TKIP or CCMP: 48-bit PN */
+       u_int64_t pn48;
+
+       /* WAPI: 128-bit PN */
+       u_int64_t pn128[2];
+};
+
+struct htt_cmd {
+       struct htt_cmd_hdr hdr;
+       union {
+               struct htt_ver_req ver_req;
+               struct htt_mgmt_tx_desc mgmt_tx;
+               struct htt_data_tx_desc data_tx;
+               struct htt_rx_ring_setup rx_setup;
+               struct htt_stats_req stats_req;
+               struct htt_oob_sync_req oob_sync_req;
+               struct htt_aggr_conf aggr_conf;
+               struct htt_frag_desc_bank_cfg frag_desc_bank_cfg;
+       };
+} __packed;
+
+struct htt_resp {
+       struct htt_resp_hdr hdr;
+       union {
+               struct htt_ver_resp ver_resp;
+               struct htt_mgmt_tx_completion mgmt_tx_completion;
+               struct htt_data_tx_completion data_tx_completion;
+               struct htt_rx_indication rx_ind;
+               struct htt_rx_fragment_indication rx_frag_ind;
+               struct htt_rx_peer_map peer_map;
+               struct htt_rx_peer_unmap peer_unmap;
+               struct htt_rx_flush rx_flush;
+               struct htt_rx_addba rx_addba;
+               struct htt_rx_delba rx_delba;
+               struct htt_security_indication security_indication;
+               struct htt_rc_update rc_update;
+               struct htt_rx_test rx_test;
+               struct htt_pktlog_msg pktlog_msg;
+               struct htt_stats_conf stats_conf;
+       };
+} __packed;
+
+
+/*** host side structures follow ***/
+
+struct htt_tx_done {
+       u32 msdu_id;
+       bool discard;
+       bool no_ack;
+};
+
+struct htt_peer_map_event {
+       u8 vdev_id;
+       u16 peer_id;
+       u8 addr[ETH_ALEN];
+};
+
+struct htt_peer_unmap_event {
+       u16 peer_id;
+};
+
+struct htt_rx_info {
+       struct sk_buff *skb;
+       enum htt_rx_mpdu_status status;
+       enum htt_rx_mpdu_encrypt_type encrypt_type;
+       s8 signal;
+       struct {
+               u8 info0;
+               u32 info1;
+               u32 info2;
+       } rate;
+       bool fcs_err;
+};
+
+struct ath10k_htt {
+       struct ath10k *ar;
+       enum ath10k_htc_ep_id eid;
+
+       int max_throughput_mbps;
+       u8 target_version_major;
+       u8 target_version_minor;
+       struct completion target_version_received;
+
+       struct {
+               /*
+                * Ring of network buffer objects - This ring is
+                * used exclusively by the host SW. This ring
+                * mirrors the dev_addrs_ring that is shared
+                * between the host SW and the MAC HW. The host SW
+                * uses this netbufs ring to locate the network
+                * buffer objects whose data buffers the HW has
+                * filled.
+                */
+               struct sk_buff **netbufs_ring;
+               /*
+                * Ring of buffer addresses -
+                * This ring holds the "physical" device address of the
+                * rx buffers the host SW provides for the MAC HW to
+                * fill.
+                */
+               __le32 *paddrs_ring;
+
+               /*
+                * Base address of ring, as a "physical" device address
+                * rather than a CPU address.
+                */
+               dma_addr_t base_paddr;
+
+               /* how many elems in the ring (power of 2) */
+               int size;
+
+               /* size - 1 */
+               unsigned size_mask;
+
+               /* how many rx buffers to keep in the ring */
+               int fill_level;
+
+               /* how many rx buffers (full+empty) are in the ring */
+               int fill_cnt;
+
+               /*
+                * alloc_idx - where HTT SW has deposited empty buffers
+                * This is allocated in consistent mem, so that the FW can
+                * read this variable, and program the HW's FW_IDX reg with
+                * the value of this shadow register.
+                */
+               struct {
+                       __le32 *vaddr;
+                       dma_addr_t paddr;
+               } alloc_idx;
+
+               /* where HTT SW has processed bufs filled by rx MAC DMA */
+               struct {
+                       unsigned msdu_payld;
+               } sw_rd_idx;
+
+               /*
+                * refill_retry_timer - timer triggered when the ring is
+                * not refilled to the level expected
+                */
+               struct timer_list refill_retry_timer;
+
+               /* Protects access to all rx ring buffer state variables */
+               spinlock_t lock;
+       } rx_ring;
+
+       unsigned int prefetch_len;
+
+       /* Protects access to %pending_tx, %used_msdu_ids */
+       spinlock_t tx_lock;
+       int max_num_pending_tx;
+       int num_pending_tx;
+       struct sk_buff **pending_tx;
+       unsigned long *used_msdu_ids; /* bitmap */
+       wait_queue_head_t empty_tx_wq;
+
+       /* set if host-fw communication goes haywire
+        * used to avoid further failures */
+       bool rx_confused;
+};
+
+#define RX_HTT_HDR_STATUS_LEN 64
+
+/* This structure layout is programmed via rx ring setup
+ * so that FW knows how to transfer the rx descriptor to the host.
+ * Buffers like this are placed on the rx ring. */
+struct htt_rx_desc {
+       union {
+               /* This field is filled on the host using the msdu buffer
+                * from htt_rx_indication */
+               struct fw_rx_desc_base fw_desc;
+               u32 pad;
+       } __packed;
+       struct {
+               struct rx_attention attention;
+               struct rx_frag_info frag_info;
+               struct rx_mpdu_start mpdu_start;
+               struct rx_msdu_start msdu_start;
+               struct rx_msdu_end msdu_end;
+               struct rx_mpdu_end mpdu_end;
+               struct rx_ppdu_start ppdu_start;
+               struct rx_ppdu_end ppdu_end;
+       } __packed;
+       u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN];
+       u8 msdu_payload[0];
+};
+
+#define HTT_RX_DESC_ALIGN 8
+
+#define HTT_MAC_ADDR_LEN 6
+
+/*
+ * FIX THIS
+ * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size,
+ * rounded up to a cache line size.
+ */
+#define HTT_RX_BUF_SIZE 1920
+#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
+
+/*
+ * DMA_MAP expects the buffer to be an integral number of cache lines.
+ * Rather than checking the actual cache line size, this code makes a
+ * conservative estimate of what the cache line size could be.
+ */
+#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_target(struct ath10k_htt *htt);
+void ath10k_htt_detach(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt);
+void ath10k_htt_tx_detach(struct ath10k_htt *htt);
+int ath10k_htt_rx_attach(struct ath10k_htt *htt);
+void ath10k_htt_rx_detach(struct ath10k_htt *htt);
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
new file mode 100644 (file)
index 0000000..de058d7
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "htc.h"
+#include "htt.h"
+#include "txrx.h"
+#include "debug.h"
+
+#include <linux/log2.h>
+
+/* slightly larger than one large A-MPDU */
+#define HTT_RX_RING_SIZE_MIN 128
+
+/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
+#define HTT_RX_RING_SIZE_MAX 2048
+
+#define HTT_RX_AVG_FRM_BYTES 1000
+
+/* ms, very conservative */
+#define HTT_RX_HOST_LATENCY_MAX_MS 20
+
+/* ms, conservative */
+#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+
+/* when under memory pressure rx ring refill may fail and needs a retry */
+#define HTT_RX_RING_REFILL_RETRY_MS 50
+
+static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
+{
+       int size;
+
+       /*
+        * It is expected that the host CPU will typically be able to
+        * service the rx indication from one A-MPDU before the rx
+        * indication from the subsequent A-MPDU happens, roughly 1-2 ms
+        * later. However, the rx ring should be sized very conservatively,
+        * to accomodate the worst reasonable delay before the host CPU
+        * services a rx indication interrupt.
+        *
+        * The rx ring need not be kept full of empty buffers. In theory,
+        * the htt host SW can dynamically track the low-water mark in the
+        * rx ring, and dynamically adjust the level to which the rx ring
+        * is filled with empty buffers, to dynamically meet the desired
+        * low-water mark.
+        *
+        * In contrast, it's difficult to resize the rx ring itself, once
+        * it's in use. Thus, the ring itself should be sized very
+        * conservatively, while the degree to which the ring is filled
+        * with empty buffers should be sized moderately conservatively.
+        */
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps +
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
+
+       if (size < HTT_RX_RING_SIZE_MIN)
+               size = HTT_RX_RING_SIZE_MIN;
+
+       if (size > HTT_RX_RING_SIZE_MAX)
+               size = HTT_RX_RING_SIZE_MAX;
+
+       size = roundup_pow_of_two(size);
+
+       return size;
+}
+
+static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
+{
+       int size;
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps *
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
+
+       /*
+        * Make sure the fill level is at least 1 less than the ring size.
+        * Leaving 1 element empty allows the SW to easily distinguish
+        * between a full ring vs. an empty ring.
+        */
+       if (size >= htt->rx_ring.size)
+               size = htt->rx_ring.size - 1;
+
+       return size;
+}
+
+static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *cb;
+       int i;
+
+       for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
+               skb = htt->rx_ring.netbufs_ring[i];
+               cb = ATH10K_SKB_CB(skb);
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+       }
+
+       htt->rx_ring.fill_cnt = 0;
+}
+
+static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       struct htt_rx_desc *rx_desc;
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int ret = 0, idx;
+
+       idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr));
+       while (num > 0) {
+               skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN))
+                       skb_pull(skb,
+                                PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) -
+                                skb->data);
+
+               /* Clear rx_desc attention word before posting to Rx ring */
+               rx_desc = (struct htt_rx_desc *)skb->data;
+               rx_desc->attention.flags = __cpu_to_le32(0);
+
+               paddr = dma_map_single(htt->ar->dev, skb->data,
+                                      skb->len + skb_tailroom(skb),
+                                      DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) {
+                       dev_kfree_skb_any(skb);
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = paddr;
+               htt->rx_ring.netbufs_ring[idx] = skb;
+               htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
+               htt->rx_ring.fill_cnt++;
+
+               num--;
+               idx++;
+               idx &= htt->rx_ring.size_mask;
+       }
+
+fail:
+       *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx);
+       return ret;
+}
+
+static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       lockdep_assert_held(&htt->rx_ring.lock);
+       return __ath10k_htt_rx_ring_fill_n(htt, num);
+}
+
+static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
+{
+       int ret, num_to_fill;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+       num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
+       if (ret == -ENOMEM) {
+               /*
+                * Failed to fill it to the desired level -
+                * we'll start a timer and try again next time.
+                * As long as enough buffers are left in the ring for
+                * another A-MPDU rx, no special recovery is needed.
+                */
+               mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
+                         msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+       }
+       spin_unlock_bh(&htt->rx_ring.lock);
+}
+
+static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
+{
+       struct ath10k_htt *htt = (struct ath10k_htt *)arg;
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
+{
+       return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
+               htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
+}
+
+void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+{
+       int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+
+       del_timer_sync(&htt->rx_ring.refill_retry_timer);
+
+       while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
+               struct sk_buff *skb =
+                               htt->rx_ring.netbufs_ring[sw_rd_idx];
+               struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
+               sw_rd_idx++;
+               sw_rd_idx &= htt->rx_ring.size_mask;
+       }
+
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+
+       kfree(htt->rx_ring.netbufs_ring);
+}
+
+static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
+{
+       int idx;
+       struct sk_buff *msdu;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+       msdu = htt->rx_ring.netbufs_ring[idx];
+
+       idx++;
+       idx &= htt->rx_ring.size_mask;
+       htt->rx_ring.sw_rd_idx.msdu_payld = idx;
+       htt->rx_ring.fill_cnt--;
+
+       spin_unlock_bh(&htt->rx_ring.lock);
+       return msdu;
+}
+
+static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
+{
+       struct sk_buff *next;
+
+       while (skb) {
+               next = skb->next;
+               dev_kfree_skb_any(skb);
+               skb = next;
+       }
+}
+
+static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
+                                  u8 **fw_desc, int *fw_desc_len,
+                                  struct sk_buff **head_msdu,
+                                  struct sk_buff **tail_msdu)
+{
+       int msdu_len, msdu_chaining = 0;
+       struct sk_buff *msdu;
+       struct htt_rx_desc *rx_desc;
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       if (htt->rx_confused) {
+               ath10k_warn("htt is confused. refusing rx\n");
+               return 0;
+       }
+
+       msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
+       while (msdu) {
+               int last_msdu, msdu_len_invalid, msdu_chained;
+
+               dma_unmap_single(htt->ar->dev,
+                                ATH10K_SKB_CB(msdu)->paddr,
+                                msdu->len + skb_tailroom(msdu),
+                                DMA_FROM_DEVICE);
+
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                               msdu->data, msdu->len + skb_tailroom(msdu));
+
+               rx_desc = (struct htt_rx_desc *)msdu->data;
+
+               /* FIXME: we must report msdu payload since this is what caller
+                *        expects now */
+               skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+               skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+
+               /*
+                * Sanity check - confirm the HW is finished filling in the
+                * rx data.
+                * If the HW and SW are working correctly, then it's guaranteed
+                * that the HW's MAC DMA is done before this point in the SW.
+                * To prevent the case that we handle a stale Rx descriptor,
+                * just assert for now until we have a way to recover.
+                */
+               if (!(__le32_to_cpu(rx_desc->attention.flags)
+                               & RX_ATTENTION_FLAGS_MSDU_DONE)) {
+                       ath10k_htt_rx_free_msdu_chain(*head_msdu);
+                       *head_msdu = NULL;
+                       msdu = NULL;
+                       ath10k_err("htt rx stopped. cannot recover\n");
+                       htt->rx_confused = true;
+                       break;
+               }
+
+               /*
+                * Copy the FW rx descriptor for this MSDU from the rx
+                * indication message into the MSDU's netbuf. HL uses the
+                * same rx indication message definition as LL, and simply
+                * appends new info (fields from the HW rx desc, and the
+                * MSDU payload itself). So, the offset into the rx
+                * indication message only has to account for the standard
+                * offset of the per-MSDU FW rx desc info within the
+                * message, and how many bytes of the per-MSDU FW rx desc
+                * info have already been consumed. (And the endianness of
+                * the host, since for a big-endian host, the rx ind
+                * message contents, including the per-MSDU rx desc bytes,
+                * were byteswapped during upload.)
+                */
+               if (*fw_desc_len > 0) {
+                       rx_desc->fw_desc.info0 = **fw_desc;
+                       /*
+                        * The target is expected to only provide the basic
+                        * per-MSDU rx descriptors. Just to be sure, verify
+                        * that the target has not attached extension data
+                        * (e.g. LRO flow ID).
+                        */
+
+                       /* or more, if there's extension data */
+                       (*fw_desc)++;
+                       (*fw_desc_len)--;
+               } else {
+                       /*
+                        * When an oversized AMSDU happened, FW will lost
+                        * some of MSDU status - in this case, the FW
+                        * descriptors provided will be less than the
+                        * actual MSDUs inside this MPDU. Mark the FW
+                        * descriptors so that it will still deliver to
+                        * upper stack, if no CRC error for this MPDU.
+                        *
+                        * FIX THIS - the FW descriptors are actually for
+                        * MSDUs in the end of this A-MSDU instead of the
+                        * beginning.
+                        */
+                       rx_desc->fw_desc.info0 = 0;
+               }
+
+               msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags)
+                                       & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
+                                          RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
+               msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
+                             RX_MSDU_START_INFO0_MSDU_LENGTH);
+               msdu_chained = rx_desc->frag_info.ring2_more_count;
+
+               if (msdu_len_invalid)
+                       msdu_len = 0;
+
+               skb_trim(msdu, 0);
+               skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
+               msdu_len -= msdu->len;
+
+               /* FIXME: Do chained buffers include htt_rx_desc or not? */
+               while (msdu_chained--) {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+
+                       dma_unmap_single(htt->ar->dev,
+                                        ATH10K_SKB_CB(next)->paddr,
+                                        next->len + skb_tailroom(next),
+                                        DMA_FROM_DEVICE);
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                                       next->data,
+                                       next->len + skb_tailroom(next));
+
+                       skb_trim(next, 0);
+                       skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
+                       msdu_len -= next->len;
+
+                       msdu->next = next;
+                       msdu = next;
+                       msdu_chaining = 1;
+               }
+
+               if (msdu_len > 0) {
+                       /* This may suggest FW bug? */
+                       ath10k_warn("htt rx msdu len not consumed (%d)\n",
+                                   msdu_len);
+               }
+
+               last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
+                               RX_MSDU_END_INFO0_LAST_MSDU;
+
+               if (last_msdu) {
+                       msdu->next = NULL;
+                       break;
+               } else {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+                       msdu->next = next;
+                       msdu = next;
+               }
+       }
+       *tail_msdu = msdu;
+
+       /*
+        * Don't refill the ring yet.
+        *
+        * First, the elements popped here are still in use - it is not
+        * safe to overwrite them until the matching call to
+        * mpdu_desc_list_next. Second, for efficiency it is preferable to
+        * refill the rx ring with 1 PPDU's worth of rx buffers (something
+        * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers
+        * (something like 3 buffers). Consequently, we'll rely on the txrx
+        * SW to tell us when it is done pulling all the PPDU's rx buffers
+        * out of the rx ring, and then refill it just once.
+        */
+
+       return msdu_chaining;
+}
+
+int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+{
+       dma_addr_t paddr;
+       void *vaddr;
+       struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
+
+       htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+       if (!is_power_of_2(htt->rx_ring.size)) {
+               ath10k_warn("htt rx ring size is not power of 2\n");
+               return -EINVAL;
+       }
+
+       htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+
+       /*
+        * Set the initial value for the level to which the rx ring
+        * should be filled, based on the max throughput and the
+        * worst likely latency for the host to fill the rx ring
+        * with new buffers. In theory, this fill level can be
+        * dynamically adjusted from the initial value set here, to
+        * reflect the actual host latency rather than a
+        * conservative assumption about the host latency.
+        */
+       htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
+
+       htt->rx_ring.netbufs_ring =
+               kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+                       GFP_KERNEL);
+       if (!htt->rx_ring.netbufs_ring)
+               goto err_netbuf;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                  (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)),
+                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_ring;
+
+       htt->rx_ring.paddrs_ring = vaddr;
+       htt->rx_ring.base_paddr = paddr;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                                  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_idx;
+
+       htt->rx_ring.alloc_idx.vaddr = vaddr;
+       htt->rx_ring.alloc_idx.paddr = paddr;
+       htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+       *htt->rx_ring.alloc_idx.vaddr = 0;
+
+       /* Initialize the Rx refill retry timer */
+       setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt);
+
+       spin_lock_init(&htt->rx_ring.lock);
+
+       htt->rx_ring.fill_cnt = 0;
+       if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
+               goto err_fill_ring;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+                  htt->rx_ring.size, htt->rx_ring.fill_level);
+       return 0;
+
+err_fill_ring:
+       ath10k_htt_rx_ring_free(htt);
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+err_dma_idx:
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+err_dma_ring:
+       kfree(htt->rx_ring.netbufs_ring);
+err_netbuf:
+       return -ENOMEM;
+}
+
+static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+       case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+               return 0;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+       case HTT_RX_MPDU_ENCRYPT_WEP128:
+       case HTT_RX_MPDU_ENCRYPT_WAPI:
+               return 0;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+/* Applies for first msdu in chain, before altering it. */
+static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt == RX_MSDU_DECAP_RAW)
+               return (void *)skb->data;
+       else
+               return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+}
+
+/* This function only applies for first msdu in an msdu chain */
+static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               if (qc[0] & 0x80)
+                       return true;
+       }
+       return false;
+}
+
+static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+                       struct htt_rx_info *info)
+{
+       struct htt_rx_desc *rxd;
+       struct sk_buff *amsdu;
+       struct sk_buff *first;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = info->skb;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+       unsigned int hdr_len;
+       int crypto_len;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       /* FIXME: No idea what assumptions are safe here. Need logs */
+       if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
+           (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOTSUPP;
+       }
+
+       /* A-MSDU max is a little less than 8K */
+       amsdu = dev_alloc_skb(8*1024);
+       if (!amsdu) {
+               ath10k_warn("A-MSDU allocation failed\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOMEM;
+       }
+
+       if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
+               int hdrlen;
+
+               hdr = (void *)rxd->rx_hdr_status;
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
+       }
+
+       first = skb;
+       while (skb) {
+               void *decap_hdr;
+               int decap_len = 0;
+
+               rxd = (void *)skb->data - sizeof(*rxd);
+               fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                               RX_MSDU_START_INFO1_DECAP_FORMAT);
+               decap_hdr = (void *)rxd->rx_hdr_status;
+
+               if (skb == first) {
+                       /* We receive linked A-MSDU subframe skbuffs. The
+                        * first one contains the original 802.11 header (and
+                        * possible crypto param) in the RX descriptor. The
+                        * A-MSDU subframe header follows that. Each part is
+                        * aligned to 4 byte boundary. */
+
+                       hdr = (void *)amsdu->data;
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
+
+                       decap_hdr += roundup(hdr_len, 4);
+                       decap_hdr += roundup(crypto_len, 4);
+               }
+
+               if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+                       /* Ethernet2 decap inserts ethernet header in place of
+                        * A-MSDU subframe header. */
+                       skb_pull(skb, 6 + 6 + 2);
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       /* Ethernet2 decap also strips the LLC/SNAP so we need
+                        * to re-insert it. The LLC/SNAP follows A-MSDU
+                        * subframe header. */
+                       /* FIXME: Not all LLCs are 8 bytes long */
+                       decap_len += 8;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
+                       /* Native Wifi decap inserts regular 802.11 header
+                        * in place of A-MSDU subframe header. */
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_RAW)
+                       skb_trim(skb, skb->len - 4); /* remove FCS */
+
+               memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+
+               /* A-MSDU subframes are padded to 4bytes
+                * but relative to first subframe, not the whole MPDU */
+               if (skb->next && ((decap_len + skb->len) & 3)) {
+                       int padlen = 4 - ((decap_len + skb->len) & 3);
+                       memset(skb_put(amsdu, padlen), 0, padlen);
+               }
+
+               skb = skb->next;
+       }
+
+       info->skb = amsdu;
+       info->encrypt_type = enctype;
+
+       ath10k_htt_rx_free_msdu_chain(first);
+
+       return 0;
+}
+
+static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+{
+       struct sk_buff *skb = info->skb;
+       struct htt_rx_desc *rxd;
+       struct ieee80211_hdr *hdr;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+
+       /* This shouldn't happen. If it does than it may be a FW bug. */
+       if (skb->next) {
+               ath10k_warn("received chained non A-MSDU frame\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+       }
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+
+       switch (fmt) {
+       case RX_MSDU_DECAP_RAW:
+               /* remove trailing FCS */
+               skb_trim(skb, skb->len - 4);
+               break;
+       case RX_MSDU_DECAP_NATIVE_WIFI:
+               /* nothing to do here */
+               break;
+       case RX_MSDU_DECAP_ETHERNET2_DIX:
+               /* macaddr[6] + macaddr[6] + ethertype[2] */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       case RX_MSDU_DECAP_8023_SNAP_LLC:
+               /* macaddr[6] + macaddr[6] + len[2] */
+               /* we don't need this for non-A-MSDU */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       }
+
+       if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+               void *llc;
+               int llclen;
+
+               llclen = 8;
+               llc  = hdr;
+               llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
+               llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+
+               skb_push(skb, llclen);
+               memcpy(skb->data, llc, llclen);
+       }
+
+       if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
+               int len = ieee80211_hdrlen(hdr->frame_control);
+               skb_push(skb, len);
+               memcpy(skb->data, hdr, len);
+       }
+
+       info->skb = skb;
+       info->encrypt_type = enctype;
+       return 0;
+}
+
+static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR)
+               return true;
+
+       return false;
+}
+
+static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_FCS_ERR)
+               return true;
+
+       return false;
+}
+
+static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
+                                 struct htt_rx_indication *rx)
+{
+       struct htt_rx_info info;
+       struct htt_rx_indication_mpdu_range *mpdu_ranges;
+       struct ieee80211_hdr *hdr;
+       int num_mpdu_ranges;
+       int fw_desc_len;
+       u8 *fw_desc;
+       int i, j;
+       int ret;
+
+       memset(&info, 0, sizeof(info));
+
+       fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
+       fw_desc = (u8 *)&rx->fw_desc;
+
+       num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+                            HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+       mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
+                       rx, sizeof(*rx) +
+                       (sizeof(struct htt_rx_indication_mpdu_range) *
+                               num_mpdu_ranges));
+
+       for (i = 0; i < num_mpdu_ranges; i++) {
+               info.status = mpdu_ranges[i].mpdu_range_status;
+
+               for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
+                       struct sk_buff *msdu_head, *msdu_tail;
+                       enum htt_rx_mpdu_status status;
+                       int msdu_chaining;
+
+                       msdu_head = NULL;
+                       msdu_tail = NULL;
+                       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt,
+                                                        &fw_desc,
+                                                        &fw_desc_len,
+                                                        &msdu_head,
+                                                        &msdu_tail);
+
+                       if (!msdu_head) {
+                               ath10k_warn("htt rx no data!\n");
+                               continue;
+                       }
+
+                       if (msdu_head->len == 0) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx dropping due to zero-len\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       status = info.status;
+
+                       /* Skip mgmt frames while we handle this in WMI */
+                       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+                           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+                           !htt->ar->monitor_enabled) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx ignoring frame w/ status %d\n",
+                                          status);
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       /* FIXME: we do not support chaining yet.
+                        * this needs investigation */
+                       if (msdu_chaining) {
+                               ath10k_warn("msdu_chaining is true\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       info.skb     = msdu_head;
+                       info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+                       info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
+                       info.signal += rx->ppdu.combined_rssi;
+
+                       info.rate.info0 = rx->ppdu.info0;
+                       info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
+                       info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+
+                       hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
+
+                       if (ath10k_htt_rx_hdr_is_amsdu(hdr))
+                               ret = ath10k_htt_rx_amsdu(htt, &info);
+                       else
+                               ret = ath10k_htt_rx_msdu(htt, &info);
+
+                       if (ret && !info.fcs_err) {
+                               ath10k_warn("error processing msdus %d\n", ret);
+                               dev_kfree_skb_any(info.skb);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
+                               ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
+                                       info.skb->data, info.skb->len);
+                       ath10k_process_rx(htt->ar, &info);
+               }
+       }
+
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
+                               struct htt_rx_fragment_indication *frag)
+{
+       struct sk_buff *msdu_head, *msdu_tail;
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+       struct htt_rx_info info = {};
+       struct ieee80211_hdr *hdr;
+       int msdu_chaining;
+       bool tkip_mic_err;
+       bool decrypt_err;
+       u8 *fw_desc;
+       int fw_desc_len, hdrlen, paramlen;
+       int trim;
+
+       fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
+       fw_desc = (u8 *)frag->fw_msdu_rx_desc;
+
+       msdu_head = NULL;
+       msdu_tail = NULL;
+       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+                                               &msdu_head, &msdu_tail);
+
+       ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
+
+       if (!msdu_head) {
+               ath10k_warn("htt rx frag no data\n");
+               return;
+       }
+
+       if (msdu_chaining || msdu_head != msdu_tail) {
+               ath10k_warn("aggregation with fragmentation?!\n");
+               ath10k_htt_rx_free_msdu_chain(msdu_head);
+               return;
+       }
+
+       /* FIXME: implement signal strength */
+
+       hdr = (struct ieee80211_hdr *)msdu_head->data;
+       rxd = (void *)msdu_head->data - sizeof(*rxd);
+       tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+       decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_DECRYPT_ERR);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt != RX_MSDU_DECAP_RAW) {
+               ath10k_warn("we dont support non-raw fragmented rx yet\n");
+               dev_kfree_skb_any(msdu_head);
+               goto end;
+       }
+
+       info.skb = msdu_head;
+       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);
+
+       if (tkip_mic_err) {
+               ath10k_warn("tkip mic error\n");
+               info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR;
+       }
+
+       if (decrypt_err) {
+               ath10k_warn("decryption err in fragmented rx\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+
+               /* It is more efficient to move the header than the payload */
+               memmove((void *)info.skb->data + paramlen,
+                       (void *)info.skb->data,
+                       hdrlen);
+               skb_pull(info.skb, paramlen);
+               hdr = (struct ieee80211_hdr *)info.skb->data;
+       }
+
+       /* remove trailing FCS */
+       trim  = 4;
+
+       /* remove crypto trailer */
+       trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+
+       /* last fragment of TKIP frags has MIC */
+       if (!ieee80211_has_morefrags(hdr->frame_control) &&
+           info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+               trim += 8;
+
+       if (trim > info.skb->len) {
+               ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       skb_trim(info.skb, info.skb->len - trim);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+                       info.skb->data, info.skb->len);
+       ath10k_process_rx(htt->ar, &info);
+
+end:
+       if (fw_desc_len > 0) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "expecting more fragmented rx in one indication %d\n",
+                          fw_desc_len);
+       }
+}
+
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ath10k_htt *htt = ar->htt;
+       struct htt_resp *resp = (struct htt_resp *)skb->data;
+
+       /* confirm alignment */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("unaligned htt message, expect trouble\n");
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+                  resp->hdr.msg_type);
+       switch (resp->hdr.msg_type) {
+       case HTT_T2H_MSG_TYPE_VERSION_CONF: {
+               htt->target_version_major = resp->ver_resp.major;
+               htt->target_version_minor = resp->ver_resp.minor;
+               complete(&htt->target_version_received);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_IND: {
+               ath10k_htt_rx_handler(htt, &resp->rx_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_MAP: {
+               struct htt_peer_map_event ev = {
+                       .vdev_id = resp->peer_map.vdev_id,
+                       .peer_id = __le16_to_cpu(resp->peer_map.peer_id),
+               };
+               memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr));
+               ath10k_peer_map_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_UNMAP: {
+               struct htt_peer_unmap_event ev = {
+                       .peer_id = __le16_to_cpu(resp->peer_unmap.peer_id),
+               };
+               ath10k_peer_unmap_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
+               struct htt_tx_done tx_done = {};
+               int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
+
+               tx_done.msdu_id =
+                       __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
+
+               switch (status) {
+               case HTT_MGMT_TX_STATUS_OK:
+                       break;
+               case HTT_MGMT_TX_STATUS_RETRY:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_MGMT_TX_STATUS_DROP:
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_txrx_tx_completed(htt, &tx_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
+               struct htt_tx_done tx_done = {};
+               int status = MS(resp->data_tx_completion.flags,
+                               HTT_DATA_TX_STATUS);
+               __le16 msdu_id;
+               int i;
+
+               switch (status) {
+               case HTT_DATA_TX_STATUS_NO_ACK:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_DATA_TX_STATUS_OK:
+                       break;
+               case HTT_DATA_TX_STATUS_DISCARD:
+               case HTT_DATA_TX_STATUS_POSTPONE:
+               case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+                       tx_done.discard = true;
+                       break;
+               default:
+                       ath10k_warn("unhandled tx completion status %d\n",
+                                   status);
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+                          resp->data_tx_completion.num_msdus);
+
+               for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+                       msdu_id = resp->data_tx_completion.msdus[i];
+                       tx_done.msdu_id = __le16_to_cpu(msdu_id);
+                       ath10k_txrx_tx_completed(htt, &tx_done);
+               }
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_SEC_IND: {
+               struct ath10k *ar = htt->ar;
+               struct htt_security_indication *ev = &resp->security_indication;
+
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "sec ind peer_id %d unicast %d type %d\n",
+                         __le16_to_cpu(ev->peer_id),
+                         !!(ev->flags & HTT_SECURITY_IS_UNICAST),
+                         MS(ev->flags, HTT_SECURITY_TYPE));
+               complete(&ar->install_key_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TEST:
+               /* FIX THIS */
+               break;
+       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
+       case HTT_T2H_MSG_TYPE_STATS_CONF:
+       case HTT_T2H_MSG_TYPE_RX_ADDBA:
+       case HTT_T2H_MSG_TYPE_RX_DELBA:
+       case HTT_T2H_MSG_TYPE_RX_FLUSH:
+       default:
+               ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n",
+                          resp->hdr.msg_type);
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               break;
+       };
+
+       /* Free the indication buffer */
+       dev_kfree_skb_any(skb);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
new file mode 100644 (file)
index 0000000..ef79106
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/etherdevice.h>
+#include "htt.h"
+#include "mac.h"
+#include "hif.h"
+#include "txrx.h"
+#include "debug.h"
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       htt->num_pending_tx--;
+       if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
+               ieee80211_wake_queues(htt->ar->hw);
+}
+
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       spin_lock_bh(&htt->tx_lock);
+       __ath10k_htt_tx_dec_pending(htt);
+       spin_unlock_bh(&htt->tx_lock);
+}
+
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+{
+       int ret = 0;
+
+       spin_lock_bh(&htt->tx_lock);
+
+       if (htt->num_pending_tx >= htt->max_num_pending_tx) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       htt->num_pending_tx++;
+       if (htt->num_pending_tx == htt->max_num_pending_tx)
+               ieee80211_stop_queues(htt->ar->hw);
+
+exit:
+       spin_unlock_bh(&htt->tx_lock);
+       return ret;
+}
+
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
+{
+       int msdu_id;
+
+       lockdep_assert_held(&htt->tx_lock);
+
+       msdu_id = find_first_zero_bit(htt->used_msdu_ids,
+                                     htt->max_num_pending_tx);
+       if (msdu_id == htt->max_num_pending_tx)
+               return -ENOBUFS;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
+       __set_bit(msdu_id, htt->used_msdu_ids);
+       return msdu_id;
+}
+
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
+{
+       lockdep_assert_held(&htt->tx_lock);
+
+       if (!test_bit(msdu_id, htt->used_msdu_ids))
+               ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+       __clear_bit(msdu_id, htt->used_msdu_ids);
+}
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+{
+       u8 pipe;
+
+       spin_lock_init(&htt->tx_lock);
+       init_waitqueue_head(&htt->empty_tx_wq);
+
+       /* At the beginning free queue number should hint us the maximum
+        * queue length */
+       pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+       htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
+                                                                  pipe);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+                  htt->max_num_pending_tx);
+
+       htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
+                                 htt->max_num_pending_tx, GFP_KERNEL);
+       if (!htt->pending_tx)
+               return -ENOMEM;
+
+       htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
+                                    BITS_TO_LONGS(htt->max_num_pending_tx),
+                                    GFP_KERNEL);
+       if (!htt->used_msdu_ids) {
+               kfree(htt->pending_tx);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+{
+       struct sk_buff *txdesc;
+       int msdu_id;
+
+       /* No locks needed. Called after communication with the device has
+        * been stopped. */
+
+       for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
+               if (!test_bit(msdu_id, htt->used_msdu_ids))
+                       continue;
+
+               txdesc = htt->pending_tx[msdu_id];
+               if (!txdesc)
+                       continue;
+
+               ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
+                          msdu_id);
+
+               if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+                       ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+
+               ATH10K_SKB_CB(txdesc)->htt.discard = true;
+               ath10k_txrx_tx_unref(htt, txdesc);
+       }
+}
+
+void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_tx_cleanup_pending(htt);
+       kfree(htt->pending_tx);
+       kfree(htt->used_msdu_ids);
+       return;
+}
+
+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;
+
+       if (skb_cb->htt.is_conf) {
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       if (skb_cb->is_aborted) {
+               skb_cb->htt.discard = true;
+
+               /* if the skbuff is aborted we need to make sure we'll free up
+                * the tx resources, we can't simply run tx_unref() 2 times
+                * because if htt tx completion came in earlier we'd access
+                * unallocated memory */
+               if (skb_cb->htt.refcount > 1)
+                       skb_cb->htt.refcount = 1;
+       }
+
+       ath10k_txrx_tx_unref(htt, skb);
+}
+
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len = 0;
+       int ret;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->ver_req);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       struct htt_rx_ring_setup_ring *ring;
+       const int num_rx_ring = 1;
+       u16 flags;
+       u32 fw_idx;
+       int len;
+       int ret;
+
+       /*
+        * the HW expects the buffer to be an integral number of 4-byte
+        * "words"
+        */
+       BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4));
+       BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0);
+
+       len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr)
+           + (sizeof(*ring) * num_rx_ring);
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+
+       cmd = (struct htt_cmd *)skb->data;
+       ring = &cmd->rx_setup.rings[0];
+
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG;
+       cmd->rx_setup.hdr.num_rings = 1;
+
+       /* FIXME: do we need all of this? */
+       flags = 0;
+       flags |= HTT_RX_RING_FLAGS_MAC80211_HDR;
+       flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD;
+       flags |= HTT_RX_RING_FLAGS_PPDU_START;
+       flags |= HTT_RX_RING_FLAGS_PPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MPDU_START;
+       flags |= HTT_RX_RING_FLAGS_MPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MSDU_START;
+       flags |= HTT_RX_RING_FLAGS_MSDU_END;
+       flags |= HTT_RX_RING_FLAGS_RX_ATTENTION;
+       flags |= HTT_RX_RING_FLAGS_FRAG_INFO;
+       flags |= HTT_RX_RING_FLAGS_UNICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_MULTICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_CTRL_RX;
+       flags |= HTT_RX_RING_FLAGS_MGMT_RX;
+       flags |= HTT_RX_RING_FLAGS_NULL_RX;
+       flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX;
+
+       fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
+
+       ring->fw_idx_shadow_reg_paddr =
+               __cpu_to_le32(htt->rx_ring.alloc_idx.paddr);
+       ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr);
+       ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size);
+       ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE);
+       ring->flags = __cpu_to_le16(flags);
+       ring->fw_idx_init_val = __cpu_to_le16(fw_idx);
+
+#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4)
+
+       ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status));
+       ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload));
+       ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start));
+       ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end));
+       ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start));
+       ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end));
+       ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start));
+       ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end));
+       ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention));
+       ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info));
+
+#undef desc_offset
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct htt_cmd *cmd;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       int len = 0;
+       int msdu_id = -1;
+       int res;
+
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->mgmt_tx);
+
+       txdesc = ath10k_htc_alloc_skb(len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       skb_put(txdesc, len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       cmd->hdr.msg_type         = HTT_H2T_MSG_TYPE_MGMT_TX;
+       cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       cmd->mgmt_tx.len        = __cpu_to_le32(msdu->len);
+       cmd->mgmt_tx.desc_id    = __cpu_to_le32(msdu_id);
+       cmd->mgmt_tx.vdev_id    = __cpu_to_le32(vdev_id);
+       memcpy(cmd->mgmt_tx.hdr, msdu->data,
+              min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+
+err:
+       ath10k_skb_unmap(dev, msdu);
+
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       return res;
+}
+
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct htt_cmd *cmd;
+       struct htt_data_tx_desc_frag *tx_frags;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct sk_buff *txfrag = NULL;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       u8 tid;
+       int prefetch_len, desc_len, frag_len;
+       dma_addr_t frags_paddr;
+       int msdu_id = -1;
+       int res;
+       u8 flags0;
+       u16 flags1;
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       prefetch_len = min(htt->prefetch_len, msdu->len);
+       prefetch_len = roundup(prefetch_len, 4);
+
+       desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
+       frag_len = sizeof(*tx_frags) * 2;
+
+       txdesc = ath10k_htc_alloc_skb(desc_len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       txfrag = dev_alloc_skb(frag_len);
+       if (!txfrag) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
+               ath10k_warn("htt alignment check failed. dropping packet.\n");
+               res = -EIO;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       /* tx fragment list must be terminated with zero-entry */
+       skb_put(txfrag, frag_len);
+       tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
+       tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       tx_frags[0].len   = __cpu_to_le32(msdu->len);
+       tx_frags[1].paddr = __cpu_to_le32(0);
+       tx_frags[1].len   = __cpu_to_le32(0);
+
+       res = ath10k_skb_map(dev, txfrag);
+       if (res)
+               goto err;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
+                  (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
+                  (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
+                       txfrag->data, frag_len);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
+                       msdu->data, msdu->len);
+
+       skb_put(txdesc, desc_len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       memset(cmd, 0, desc_len);
+
+       tid = ATH10K_SKB_CB(msdu)->htt.tid;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+
+       flags0  = 0;
+       if (!ieee80211_has_protected(hdr->frame_control))
+               flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+       flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
+       flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+                    HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+       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);
+
+       frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
+
+       cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
+       cmd->data_tx.flags0      = flags0;
+       cmd->data_tx.flags1      = __cpu_to_le16(flags1);
+       cmd->data_tx.len         = __cpu_to_le16(msdu->len);
+       cmd->data_tx.id          = __cpu_to_le16(msdu_id);
+       cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+       cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
+
+       memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.txfrag = txfrag;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+err:
+       if (txfrag)
+               ath10k_skb_unmap(dev, txfrag);
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (txfrag)
+               dev_kfree_skb_any(txfrag);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       ath10k_skb_unmap(dev, msdu);
+       return res;
+}
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
new file mode 100644 (file)
index 0000000..44ed5af
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _HW_H_
+#define _HW_H_
+
+#include "targaddrs.h"
+
+/* Supported FW version */
+#define SUPPORTED_FW_MAJOR     1
+#define SUPPORTED_FW_MINOR     0
+#define SUPPORTED_FW_RELEASE   0
+#define SUPPORTED_FW_BUILD     629
+
+/* QCA988X 1.0 definitions */
+#define QCA988X_HW_1_0_VERSION         0x4000002c
+#define QCA988X_HW_1_0_FW_DIR          "ath10k/QCA988X/hw1.0"
+#define QCA988X_HW_1_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_1_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234
+
+/* QCA988X 2.0 definitions */
+#define QCA988X_HW_2_0_VERSION         0x4100016c
+#define QCA988X_HW_2_0_FW_DIR          "ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_2_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
+
+/* Known pecularities:
+ *  - current FW doesn't support raw rx mode (last tested v599)
+ *  - current FW dumps upon raw tx mode (last tested v599)
+ *  - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
+ *  - raw have FCS, nwifi doesn't
+ *  - ethernet frames have 802.11 header decapped and parts (base hdr, cipher
+ *    param, llc/snap) are aligned to 4byte boundaries each */
+enum ath10k_hw_txrx_mode {
+       ATH10K_HW_TXRX_RAW = 0,
+       ATH10K_HW_TXRX_NATIVE_WIFI = 1,
+       ATH10K_HW_TXRX_ETHERNET = 2,
+};
+
+enum ath10k_mcast2ucast_mode {
+       ATH10K_MCAST2UCAST_DISABLED = 0,
+       ATH10K_MCAST2UCAST_ENABLED = 1,
+};
+
+#define TARGET_NUM_VDEVS                       8
+#define TARGET_NUM_PEER_AST                    2
+#define TARGET_NUM_WDS_ENTRIES                 32
+#define TARGET_DMA_BURST_SIZE                  0
+#define TARGET_MAC_AGGR_DELIM                  0
+#define TARGET_AST_SKID_LIMIT                  16
+#define TARGET_NUM_PEERS                       16
+#define TARGET_NUM_OFFLOAD_PEERS               0
+#define TARGET_NUM_OFFLOAD_REORDER_BUFS         0
+#define TARGET_NUM_PEER_KEYS                   2
+#define TARGET_NUM_TIDS                (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_TX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_TIMEOUT_LO_PRI               100
+#define TARGET_RX_TIMEOUT_HI_PRI               40
+#define TARGET_RX_DECAP_MODE                   ATH10K_HW_TXRX_ETHERNET
+#define TARGET_SCAN_MAX_PENDING_REQS           4
+#define TARGET_BMISS_OFFLOAD_MAX_VDEV          3
+#define TARGET_ROAM_OFFLOAD_MAX_VDEV           3
+#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES    8
+#define TARGET_GTK_OFFLOAD_MAX_VDEV            3
+#define TARGET_NUM_MCAST_GROUPS                        0
+#define TARGET_NUM_MCAST_TABLE_ELEMS           0
+#define TARGET_MCAST2UCAST_MODE                        ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_TX_DBG_LOG_SIZE                 1024
+#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0
+#define TARGET_VOW_CONFIG                      0
+#define TARGET_NUM_MSDU_DESC                   (1024 + 400)
+#define TARGET_MAX_FRAG_ENTRIES                        0
+
+
+/* Number of Copy Engines supported */
+#define CE_COUNT 8
+
+/*
+ * Total number of PCIe MSI interrupts requested for all interrupt sources.
+ * PCIe standard forces this to be a power of 2.
+ * Some Host OS's limit MSI requests that can be granted to 8
+ * so for now we abide by this limit and avoid requesting more
+ * than that.
+ */
+#define MSI_NUM_REQUEST_LOG2   3
+#define MSI_NUM_REQUEST                (1<<MSI_NUM_REQUEST_LOG2)
+
+/*
+ * Granted MSIs are assigned as follows:
+ * Firmware uses the first
+ * Remaining MSIs, if any, are used by Copy Engines
+ * This mapping is known to both Target firmware and Host software.
+ * It may be changed as long as Host and Target are kept in sync.
+ */
+/* MSI for firmware (errors, etc.) */
+#define MSI_ASSIGN_FW          0
+
+/* MSIs for Copy Engines */
+#define MSI_ASSIGN_CE_INITIAL  1
+#define MSI_ASSIGN_CE_MAX      7
+
+/* as of IP3.7.1 */
+#define RTC_STATE_V_ON                         3
+
+#define RTC_STATE_COLD_RESET_MASK              0x00000400
+#define RTC_STATE_V_LSB                                0
+#define RTC_STATE_V_MASK                       0x00000007
+#define RTC_STATE_ADDRESS                      0x0000
+#define PCIE_SOC_WAKE_V_MASK                   0x00000001
+#define PCIE_SOC_WAKE_ADDRESS                  0x0004
+#define PCIE_SOC_WAKE_RESET                    0x00000000
+#define SOC_GLOBAL_RESET_ADDRESS               0x0008
+
+#define RTC_SOC_BASE_ADDRESS                   0x00004000
+#define RTC_WMAC_BASE_ADDRESS                  0x00005000
+#define MAC_COEX_BASE_ADDRESS                  0x00006000
+#define BT_COEX_BASE_ADDRESS                   0x00007000
+#define SOC_PCIE_BASE_ADDRESS                  0x00008000
+#define SOC_CORE_BASE_ADDRESS                  0x00009000
+#define WLAN_UART_BASE_ADDRESS                 0x0000c000
+#define WLAN_SI_BASE_ADDRESS                   0x00010000
+#define WLAN_GPIO_BASE_ADDRESS                 0x00014000
+#define WLAN_ANALOG_INTF_BASE_ADDRESS          0x0001c000
+#define WLAN_MAC_BASE_ADDRESS                  0x00020000
+#define EFUSE_BASE_ADDRESS                     0x00030000
+#define FPGA_REG_BASE_ADDRESS                  0x00039000
+#define WLAN_UART2_BASE_ADDRESS                        0x00054c00
+#define CE_WRAPPER_BASE_ADDRESS                        0x00057000
+#define CE0_BASE_ADDRESS                       0x00057400
+#define CE1_BASE_ADDRESS                       0x00057800
+#define CE2_BASE_ADDRESS                       0x00057c00
+#define CE3_BASE_ADDRESS                       0x00058000
+#define CE4_BASE_ADDRESS                       0x00058400
+#define CE5_BASE_ADDRESS                       0x00058800
+#define CE6_BASE_ADDRESS                       0x00058c00
+#define CE7_BASE_ADDRESS                       0x00059000
+#define DBI_BASE_ADDRESS                       0x00060000
+#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS     0x0006c000
+#define PCIE_LOCAL_BASE_ADDRESS                        0x00080000
+
+#define SOC_RESET_CONTROL_OFFSET               0x00000000
+#define SOC_RESET_CONTROL_SI0_RST_MASK         0x00000001
+#define SOC_CPU_CLOCK_OFFSET                   0x00000020
+#define SOC_CPU_CLOCK_STANDARD_LSB             0
+#define SOC_CPU_CLOCK_STANDARD_MASK            0x00000003
+#define SOC_CLOCK_CONTROL_OFFSET               0x00000028
+#define SOC_CLOCK_CONTROL_SI0_CLK_MASK         0x00000001
+#define SOC_SYSTEM_SLEEP_OFFSET                        0x000000c4
+#define SOC_LPO_CAL_OFFSET                     0x000000e0
+#define SOC_LPO_CAL_ENABLE_LSB                 20
+#define SOC_LPO_CAL_ENABLE_MASK                        0x00100000
+
+#define WLAN_RESET_CONTROL_COLD_RST_MASK       0x00000008
+#define WLAN_RESET_CONTROL_WARM_RST_MASK       0x00000004
+#define WLAN_SYSTEM_SLEEP_DISABLE_LSB          0
+#define WLAN_SYSTEM_SLEEP_DISABLE_MASK         0x00000001
+
+#define WLAN_GPIO_PIN0_ADDRESS                 0x00000028
+#define WLAN_GPIO_PIN0_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN1_ADDRESS                 0x0000002c
+#define WLAN_GPIO_PIN1_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN10_ADDRESS                        0x00000050
+#define WLAN_GPIO_PIN11_ADDRESS                        0x00000054
+#define WLAN_GPIO_PIN12_ADDRESS                        0x00000058
+#define WLAN_GPIO_PIN13_ADDRESS                        0x0000005c
+
+#define CLOCK_GPIO_OFFSET                      0xffffffff
+#define CLOCK_GPIO_BT_CLK_OUT_EN_LSB           0
+#define CLOCK_GPIO_BT_CLK_OUT_EN_MASK          0
+
+#define SI_CONFIG_OFFSET                       0x00000000
+#define SI_CONFIG_BIDIR_OD_DATA_LSB            18
+#define SI_CONFIG_BIDIR_OD_DATA_MASK           0x00040000
+#define SI_CONFIG_I2C_LSB                      16
+#define SI_CONFIG_I2C_MASK                     0x00010000
+#define SI_CONFIG_POS_SAMPLE_LSB               7
+#define SI_CONFIG_POS_SAMPLE_MASK              0x00000080
+#define SI_CONFIG_INACTIVE_DATA_LSB            5
+#define SI_CONFIG_INACTIVE_DATA_MASK           0x00000020
+#define SI_CONFIG_INACTIVE_CLK_LSB             4
+#define SI_CONFIG_INACTIVE_CLK_MASK            0x00000010
+#define SI_CONFIG_DIVIDER_LSB                  0
+#define SI_CONFIG_DIVIDER_MASK                 0x0000000f
+#define SI_CS_OFFSET                           0x00000004
+#define SI_CS_DONE_ERR_MASK                    0x00000400
+#define SI_CS_DONE_INT_MASK                    0x00000200
+#define SI_CS_START_LSB                                8
+#define SI_CS_START_MASK                       0x00000100
+#define SI_CS_RX_CNT_LSB                       4
+#define SI_CS_RX_CNT_MASK                      0x000000f0
+#define SI_CS_TX_CNT_LSB                       0
+#define SI_CS_TX_CNT_MASK                      0x0000000f
+
+#define SI_TX_DATA0_OFFSET                     0x00000008
+#define SI_TX_DATA1_OFFSET                     0x0000000c
+#define SI_RX_DATA0_OFFSET                     0x00000010
+#define SI_RX_DATA1_OFFSET                     0x00000014
+
+#define CORE_CTRL_CPU_INTR_MASK                        0x00002000
+#define CORE_CTRL_ADDRESS                      0x0000
+#define PCIE_INTR_ENABLE_ADDRESS               0x0008
+#define PCIE_INTR_CLR_ADDRESS                  0x0014
+#define SCRATCH_3_ADDRESS                      0x0030
+
+/* Firmware indications to the Host via SCRATCH_3 register. */
+#define FW_INDICATOR_ADDRESS   (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
+#define FW_IND_EVENT_PENDING                   1
+#define FW_IND_INITIALIZED                     2
+
+/* HOST_REG interrupt from firmware */
+#define PCIE_INTR_FIRMWARE_MASK                        0x00000400
+#define PCIE_INTR_CE_MASK_ALL                  0x0007f800
+
+#define DRAM_BASE_ADDRESS                      0x00400000
+
+#define MISSING 0
+
+#define SYSTEM_SLEEP_OFFSET                    SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_SYSTEM_SLEEP_OFFSET               SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_RESET_CONTROL_OFFSET              SOC_RESET_CONTROL_OFFSET
+#define CLOCK_CONTROL_OFFSET                   SOC_CLOCK_CONTROL_OFFSET
+#define CLOCK_CONTROL_SI0_CLK_MASK             SOC_CLOCK_CONTROL_SI0_CLK_MASK
+#define RESET_CONTROL_MBOX_RST_MASK            MISSING
+#define RESET_CONTROL_SI0_RST_MASK             SOC_RESET_CONTROL_SI0_RST_MASK
+#define GPIO_BASE_ADDRESS                      WLAN_GPIO_BASE_ADDRESS
+#define GPIO_PIN0_OFFSET                       WLAN_GPIO_PIN0_ADDRESS
+#define GPIO_PIN1_OFFSET                       WLAN_GPIO_PIN1_ADDRESS
+#define GPIO_PIN0_CONFIG_MASK                  WLAN_GPIO_PIN0_CONFIG_MASK
+#define GPIO_PIN1_CONFIG_MASK                  WLAN_GPIO_PIN1_CONFIG_MASK
+#define SI_BASE_ADDRESS                                WLAN_SI_BASE_ADDRESS
+#define SCRATCH_BASE_ADDRESS                   SOC_CORE_BASE_ADDRESS
+#define LOCAL_SCRATCH_OFFSET                   0x18
+#define CPU_CLOCK_OFFSET                       SOC_CPU_CLOCK_OFFSET
+#define LPO_CAL_OFFSET                         SOC_LPO_CAL_OFFSET
+#define GPIO_PIN10_OFFSET                      WLAN_GPIO_PIN10_ADDRESS
+#define GPIO_PIN11_OFFSET                      WLAN_GPIO_PIN11_ADDRESS
+#define GPIO_PIN12_OFFSET                      WLAN_GPIO_PIN12_ADDRESS
+#define GPIO_PIN13_OFFSET                      WLAN_GPIO_PIN13_ADDRESS
+#define CPU_CLOCK_STANDARD_LSB                 SOC_CPU_CLOCK_STANDARD_LSB
+#define CPU_CLOCK_STANDARD_MASK                        SOC_CPU_CLOCK_STANDARD_MASK
+#define LPO_CAL_ENABLE_LSB                     SOC_LPO_CAL_ENABLE_LSB
+#define LPO_CAL_ENABLE_MASK                    SOC_LPO_CAL_ENABLE_MASK
+#define ANALOG_INTF_BASE_ADDRESS               WLAN_ANALOG_INTF_BASE_ADDRESS
+#define MBOX_BASE_ADDRESS                      MISSING
+#define INT_STATUS_ENABLE_ERROR_LSB            MISSING
+#define INT_STATUS_ENABLE_ERROR_MASK           MISSING
+#define INT_STATUS_ENABLE_CPU_LSB              MISSING
+#define INT_STATUS_ENABLE_CPU_MASK             MISSING
+#define INT_STATUS_ENABLE_COUNTER_LSB          MISSING
+#define INT_STATUS_ENABLE_COUNTER_MASK         MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_LSB                MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_MASK       MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB   MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK  MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB    MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK   MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_LSB      MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_MASK     MISSING
+#define INT_STATUS_ENABLE_ADDRESS              MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_LSB          MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_MASK         MISSING
+#define HOST_INT_STATUS_ADDRESS                        MISSING
+#define CPU_INT_STATUS_ADDRESS                 MISSING
+#define ERROR_INT_STATUS_ADDRESS               MISSING
+#define ERROR_INT_STATUS_WAKEUP_MASK           MISSING
+#define ERROR_INT_STATUS_WAKEUP_LSB            MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK     MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_MASK      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_LSB       MISSING
+#define COUNT_DEC_ADDRESS                      MISSING
+#define HOST_INT_STATUS_CPU_MASK               MISSING
+#define HOST_INT_STATUS_CPU_LSB                        MISSING
+#define HOST_INT_STATUS_ERROR_MASK             MISSING
+#define HOST_INT_STATUS_ERROR_LSB              MISSING
+#define HOST_INT_STATUS_COUNTER_MASK           MISSING
+#define HOST_INT_STATUS_COUNTER_LSB            MISSING
+#define RX_LOOKAHEAD_VALID_ADDRESS             MISSING
+#define WINDOW_DATA_ADDRESS                    MISSING
+#define WINDOW_READ_ADDR_ADDRESS               MISSING
+#define WINDOW_WRITE_ADDR_ADDRESS              MISSING
+
+#define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB)
+
+#endif /* _HW_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
new file mode 100644 (file)
index 0000000..3446c98
--- /dev/null
@@ -0,0 +1,3066 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "mac.h"
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+
+#include "core.h"
+#include "debug.h"
+#include "wmi.h"
+#include "htt.h"
+#include "txrx.h"
+
+/**********/
+/* Crypto */
+/**********/
+
+static int ath10k_send_key(struct ath10k_vif *arvif,
+                          struct ieee80211_key_conf *key,
+                          enum set_key_cmd cmd,
+                          const u8 *macaddr)
+{
+       struct wmi_vdev_install_key_arg arg = {
+               .vdev_id = arvif->vdev_id,
+               .key_idx = key->keyidx,
+               .key_len = key->keylen,
+               .key_data = key->key,
+               .macaddr = macaddr,
+       };
+
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+               arg.key_flags = WMI_KEY_PAIRWISE;
+       else
+               arg.key_flags = WMI_KEY_GROUP;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               arg.key_cipher = WMI_CIPHER_AES_CCM;
+               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               arg.key_cipher = WMI_CIPHER_TKIP;
+               arg.key_txmic_len = 8;
+               arg.key_rxmic_len = 8;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               arg.key_cipher = WMI_CIPHER_WEP;
+               /* AP/IBSS mode requires self-key to be groupwise
+                * Otherwise pairwise key must be set */
+               if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
+                       arg.key_flags = WMI_KEY_PAIRWISE;
+               break;
+       default:
+               ath10k_warn("cipher %d is not supported\n", key->cipher);
+               return -EOPNOTSUPP;
+       }
+
+       if (cmd == DISABLE_KEY) {
+               arg.key_cipher = WMI_CIPHER_NONE;
+               arg.key_data = NULL;
+       }
+
+       return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+}
+
+static int ath10k_install_key(struct ath10k_vif *arvif,
+                             struct ieee80211_key_conf *key,
+                             enum set_key_cmd cmd,
+                             const u8 *macaddr)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       INIT_COMPLETION(ar->install_key_done);
+
+       ret = ath10k_send_key(arvif, key, cmd, macaddr);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
+                                       const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+               if (arvif->wep_keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
+                                        addr);
+               if (ret)
+                       return ret;
+
+               peer->keys[i] = arvif->wep_keys[i];
+       }
+
+       return 0;
+}
+
+static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
+                                 const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+               if (peer->keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, peer->keys[i],
+                                        DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove peer wep key %d (%d)\n",
+                                   i, ret);
+
+               peer->keys[i] = NULL;
+       }
+
+       return first_errno;
+}
+
+static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
+                                struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       u8 addr[ETH_ALEN];
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       for (;;) {
+               /* since ath10k_install_key we can't hold data_lock all the
+                * time, so we try to remove the keys incrementally */
+               spin_lock_bh(&ar->data_lock);
+               i = 0;
+               list_for_each_entry(peer, &ar->peers, list) {
+                       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+                               if (peer->keys[i] == key) {
+                                       memcpy(addr, peer->addr, ETH_ALEN);
+                                       peer->keys[i] = NULL;
+                                       break;
+                               }
+                       }
+
+                       if (i < ARRAY_SIZE(peer->keys))
+                               break;
+               }
+               spin_unlock_bh(&ar->data_lock);
+
+               if (i == ARRAY_SIZE(peer->keys))
+                       break;
+
+               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove key for %pM\n", addr);
+       }
+
+       return first_errno;
+}
+
+
+/*********************/
+/* General utilities */
+/*********************/
+
+static inline enum wmi_phy_mode
+chan_to_phymode(const struct cfg80211_chan_def *chandef)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       switch (chandef->chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11G;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NG_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NG_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_80:
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       case IEEE80211_BAND_5GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11A;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NA_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NA_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_80:
+                       phymode = MODE_11AC_VHT80;
+                       break;
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       WARN_ON(phymode == MODE_UNKNOWN);
+       return phymode;
+}
+
+static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
+{
+/*
+ * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
+ *   0 for no restriction
+ *   1 for 1/4 us
+ *   2 for 1/2 us
+ *   3 for 1 us
+ *   4 for 2 us
+ *   5 for 4 us
+ *   6 for 8 us
+ *   7 for 16 us
+ */
+       switch (mpdudensity) {
+       case 0:
+               return 0;
+       case 1:
+       case 2:
+       case 3:
+       /* Our lower layer calculations limit our precision to
+          1 microsecond */
+               return 1;
+       case 4:
+               return 2;
+       case 5:
+               return 4;
+       case 6:
+               return 8;
+       case 7:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
+{
+       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) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+
+               ath10k_warn("removing stale peer %pM from vdev_id %d\n",
+                           peer->addr, vdev_id);
+
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
+/************************/
+/* Interface management */
+/************************/
+
+static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_for_completion_timeout(&ar->vdev_setup_done,
+                                         ATH10K_VDEV_SETUP_TIMEOUT_HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_conf *conf = &ar->hw->conf;
+       struct ieee80211_channel *channel = conf->chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.dtim_period = arvif->dtim_period;
+       arg.bcn_intval = arvif->beacon_interval;
+
+       arg.channel.freq = channel->center_freq;
+
+       arg.channel.band_center_freq1 = conf->chandef.center_freq1;
+
+       arg.channel.mode = chan_to_phymode(&conf->chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               arg.ssid = arvif->u.ap.ssid;
+               arg.ssid_len = arvif->u.ap.ssid_len;
+               arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+       } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+               arg.ssid = arvif->vif->bss_conf.ssid;
+               arg.ssid_len = arvif->vif->bss_conf.ssid_len;
+       }
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("WMI vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev stop failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+{
+       struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       enum nl80211_channel_type type;
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
+
+       arg.vdev_id = vdev_id;
+       arg.channel.freq = channel->center_freq;
+       arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+
+       /* TODO setup this dynamically, what in case we
+          don't have any vifs? */
+       arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("Monitor vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("Monitor vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("Monitor vdev up failed: %d\n", ret);
+               goto vdev_stop;
+       }
+
+       ar->monitor_vdev_id = vdev_id;
+       ar->monitor_enabled = true;
+
+       return 0;
+
+vdev_stop:
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       return ret;
+}
+
+static int ath10k_monitor_stop(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* For some reasons, ath10k_wmi_vdev_down() here couse
+        * often ath10k_wmi_vdev_stop() to fail. Next we could
+        * not run monitor vdev and driver reload
+        * required. Don't see such problems we skip
+        * ath10k_wmi_vdev_down() here.
+        */
+
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret)
+               ath10k_warn("Monitor_down sync failed: %d\n", ret);
+
+       ar->monitor_enabled = false;
+       return ret;
+}
+
+static int ath10k_monitor_create(struct ath10k *ar)
+{
+       int bit, ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->monitor_present) {
+               ath10k_warn("Monitor mode already enabled\n");
+               return 0;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ath10k_warn("No free VDEV slots\n");
+               return -ENOMEM;
+       }
+
+       ar->monitor_vdev_id = bit - 1;
+       ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+
+       ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
+                                    WMI_VDEV_TYPE_MONITOR,
+                                    0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret);
+               goto vdev_fail;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+
+       ar->monitor_present = true;
+       return 0;
+
+vdev_fail:
+       /*
+        * Restore the ID to the global map.
+        */
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       return ret;
+}
+
+static int ath10k_monitor_destroy(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (!ar->monitor_present)
+               return 0;
+
+       ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor delete failed: %d\n", ret);
+               return ret;
+       }
+
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       ar->monitor_present = false;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+       return ret;
+}
+
+static void ath10k_control_beaconing(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info)
+{
+       int ret = 0;
+
+       if (!info->enable_beacon) {
+               ath10k_vdev_stop(arvif);
+               return;
+       }
+
+       arvif->tx_seq_no = 0x1000;
+
+       ret = ath10k_vdev_start(arvif);
+       if (ret)
+               return;
+
+       ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+       if (ret) {
+               ath10k_warn("Failed to bring up VDEV: %d\n",
+                           arvif->vdev_id);
+               return;
+       }
+       ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+}
+
+static void ath10k_control_ibss(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info,
+                               const u8 self_peer[ETH_ALEN])
+{
+       int ret = 0;
+
+       if (!info->ibss_joined) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
+               if (ret)
+                       ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                                   self_peer, arvif->vdev_id, ret);
+
+               if (is_zero_ether_addr(arvif->u.ibss.bssid))
+                       return;
+
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
+                                        arvif->u.ibss.bssid);
+               if (ret) {
+                       ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+                                   arvif->u.ibss.bssid, arvif->vdev_id, ret);
+                       return;
+               }
+
+               memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+
+               return;
+       }
+
+       ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
+       if (ret) {
+               ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                           self_peer, arvif->vdev_id, ret);
+               return;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_ATIM_WINDOW,
+                                       ATH10K_DEFAULT_ATIM);
+       if (ret)
+               ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
+                           arvif->vdev_id, ret);
+}
+
+/*
+ * Review this when mac80211 gains per-interface powersave support.
+ */
+static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       enum wmi_sta_ps_mode psmode;
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (conf->flags & IEEE80211_CONF_PS) {
+               psmode = WMI_STA_PS_MODE_ENABLED;
+               param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
+
+               ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
+                                                 arvif->vdev_id,
+                                                 param,
+                                                 conf->dynamic_ps_timeout);
+               if (ret) {
+                       ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
+                                   arvif->vdev_id);
+                       return;
+               }
+
+               ar_iter->ret = ret;
+       } else {
+               psmode = WMI_STA_PS_MODE_DISABLED;
+       }
+
+       ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
+                                            psmode);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
+                           psmode, arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
+                          psmode, arvif->vdev_id);
+}
+
+/**********************/
+/* Station management */
+/**********************/
+
+static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
+                                     struct ath10k_vif *arvif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_bss_conf *bss_conf,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       memcpy(arg->addr, sta->addr, ETH_ALEN);
+       arg->vdev_id = arvif->vdev_id;
+       arg->peer_aid = sta->aid;
+       arg->peer_flags |= WMI_PEER_AUTH;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               /*
+                * Seems FW have problems with Power Save in STA
+                * mode when we setup this parameter to high (eg. 5).
+                * Often we see that FW don't send NULL (with clean P flags)
+                * frame even there is info about buffered frames in beacons.
+                * Sometimes we have to wait more than 10 seconds before FW
+                * will wakeup. Often sending one ping from AP to our device
+                * just fail (more than 50%).
+                *
+                * Seems setting this FW parameter to 1 couse FW
+                * will check every beacon and will wakup immediately
+                * after detection buffered data.
+                */
+               arg->peer_listen_intval = 1;
+       else
+               arg->peer_listen_intval = ar->hw->conf.listen_interval;
+
+       arg->peer_num_spatial_streams = 1;
+
+       /*
+        * The assoc capabilities are available only in managed mode.
+        */
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
+               arg->peer_caps = bss_conf->assoc_capability;
+}
+
+static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct ieee80211_vif *vif = arvif->vif;
+       struct ieee80211_bss_conf *info = &vif->bss_conf;
+       struct cfg80211_bss *bss;
+       const u8 *rsnie = NULL;
+       const u8 *wpaie = NULL;
+
+       bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
+                              info->bssid, NULL, 0, 0, 0);
+       if (bss) {
+               const struct cfg80211_bss_ies *ies;
+
+               rcu_read_lock();
+               rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+               ies = rcu_dereference(bss->ies);
+
+               wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                               WLAN_OUI_TYPE_MICROSOFT_WPA,
+                               ies->data,
+                               ies->len);
+               rcu_read_unlock();
+               cfg80211_put_bss(ar->hw->wiphy, bss);
+       }
+
+       /* FIXME: base on RSN IE/WPA IE is a correct idea? */
+       if (rsnie || wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+       }
+
+       if (wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+       }
+}
+
+static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
+                                     struct ieee80211_sta *sta,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
+       const struct ieee80211_supported_band *sband;
+       const struct ieee80211_rate *rates;
+       u32 ratemask;
+       int i;
+
+       sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
+       ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
+       rates = sband->bitrates;
+
+       rateset->num_rates = 0;
+
+       for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
+               if (!(ratemask & 1))
+                       continue;
+
+               rateset->rates[rateset->num_rates] = rates->hw_value;
+               rateset->num_rates++;
+       }
+}
+
+static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
+                                  struct ieee80211_sta *sta,
+                                  struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       int smps;
+       int i, n;
+
+       if (!ht_cap->ht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_HT;
+       arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                   ht_cap->ampdu_factor)) - 1;
+
+       arg->peer_mpdu_density =
+               ath10k_parse_mpdudensity(ht_cap->ampdu_density);
+
+       arg->peer_ht_caps = ht_cap->cap;
+       arg->peer_rate_caps |= WMI_RC_HT_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
+               arg->peer_flags |= WMI_PEER_LDPC;
+
+       if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+               arg->peer_flags |= WMI_PEER_40MHZ;
+               arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
+               arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
+               u32 stbc;
+               stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
+               stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
+               arg->peer_rate_caps |= stbc;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+       smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
+       } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
+       }
+
+       if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
+               arg->peer_rate_caps |= WMI_RC_TS_FLAG;
+       else if (ht_cap->mcs.rx_mask[1])
+               arg->peer_rate_caps |= WMI_RC_DS_FLAG;
+
+       for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++)
+               if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
+                       arg->peer_ht_rates.rates[n++] = i;
+
+       arg->peer_ht_rates.num_rates = n;
+       arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+                  arg->peer_ht_rates.num_rates,
+                  arg->peer_num_spatial_streams);
+}
+
+static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct ieee80211_sta *sta,
+                                      struct ieee80211_bss_conf *bss_conf,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       u32 uapsd = 0;
+       u32 max_sp = 0;
+
+       if (sta->wme)
+               arg->peer_flags |= WMI_PEER_QOS;
+
+       if (sta->wme && sta->uapsd_queues) {
+               ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+                          sta->uapsd_queues, sta->max_sp);
+
+               arg->peer_flags |= WMI_PEER_APSD;
+               arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+                       uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+                       uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+                       uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+                       uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
+
+
+               if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
+                       max_sp = sta->max_sp;
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_UAPSD,
+                                          uapsd);
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_MAX_SP,
+                                          max_sp);
+
+               /* TODO setup this based on STA listen interval and
+                  beacon interval. Currently we don't know
+                  sta->listen_interval - mac80211 patch required.
+                  Currently use 10 seconds */
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
+                                          10);
+       }
+}
+
+static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       if (bss_conf->qos)
+               arg->peer_flags |= WMI_PEER_QOS;
+}
+
+static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
+                                   struct ieee80211_sta *sta,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+       if (!vht_cap->vht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_VHT;
+
+       arg->peer_vht_caps = vht_cap->cap;
+
+       if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+               arg->peer_flags |= WMI_PEER_80MHZ;
+
+       arg->peer_vht_rates.rx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
+       arg->peer_vht_rates.rx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+       arg->peer_vht_rates.tx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+       arg->peer_vht_rates.tx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+}
+
+static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
+                                   struct ath10k_vif *arvif,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_bss_conf *bss_conf,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       switch (arvif->vdev_type) {
+       case WMI_VDEV_TYPE_AP:
+               ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+               break;
+       case WMI_VDEV_TYPE_STA:
+               ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       /* FIXME: add VHT */
+
+       switch (ar->hw->conf.chandef.chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NG_HT40;
+                       else
+                               phymode = MODE_11NG_HT20;
+               } else {
+                       phymode = MODE_11G;
+               }
+
+               break;
+       case IEEE80211_BAND_5GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NA_HT40;
+                       else
+                               phymode = MODE_11NA_HT20;
+               } else {
+                       phymode = MODE_11A;
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       arg->peer_phymode = phymode;
+       WARN_ON(phymode == MODE_UNKNOWN);
+}
+
+static int ath10k_peer_assoc(struct ath10k *ar,
+                            struct ath10k_vif *arvif,
+                            struct ieee80211_sta *sta,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct wmi_peer_assoc_complete_arg arg;
+
+       memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+
+       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
+       ath10k_peer_assoc_h_rates(ar, sta, &arg);
+       ath10k_peer_assoc_h_ht(ar, sta, &arg);
+       ath10k_peer_assoc_h_vht(ar, sta, &arg);
+       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+
+       return ath10k_wmi_peer_assoc(ar, &arg);
+}
+
+/* can be called only in mac80211 callbacks due to `key_count` usage */
+static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta *ap_sta;
+       int ret;
+
+       rcu_read_lock();
+
+       ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
+       if (!ap_sta) {
+               ath10k_warn("Failed to find station entry for %pM\n",
+                           bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       rcu_read_unlock();
+
+       ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
+                                bss_conf->bssid);
+       if (ret)
+               ath10k_warn("VDEV: %d up failed: ret %d\n",
+                           arvif->vdev_id, ret);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "VDEV: %d associated, BSSID: %pM, AID: %d\n",
+                          arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+}
+
+/*
+ * FIXME: flush TIDs
+ */
+static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       /*
+        * For some reason, calling VDEV-DOWN before VDEV-STOP
+        * makes the FW to send frames via HTT after disassociation.
+        * No idea why this happens, even though VDEV-DOWN is supposed
+        * to be analogous to link down, so just stop the VDEV.
+        */
+       ret = ath10k_vdev_stop(arvif);
+       if (!ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
+                          arvif->vdev_id);
+
+       /*
+        * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
+        * report beacons from previously associated network through HTT.
+        * This in turn would spam mac80211 WARN_ON if we bring down all
+        * interfaces as it expects there is no rx when no interface is
+        * running.
+        */
+       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
+                          arvif->vdev_id, ret);
+
+       ath10k_wmi_flush_tx(ar);
+
+       arvif->def_wep_key_index = 0;
+}
+
+static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                               struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+       if (ret) {
+               ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+               return ret;
+       }
+
+       ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not install peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                                  struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_clear_peer_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**************/
+/* Regulatory */
+/**************/
+
+static int ath10k_update_channel_list(struct ath10k *ar)
+{
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_supported_band **bands;
+       enum ieee80211_band band;
+       struct ieee80211_channel *channel;
+       struct wmi_scan_chan_list_arg arg = {0};
+       struct wmi_channel_arg *ch;
+       bool passive;
+       int len;
+       int ret;
+       int i;
+
+       bands = hw->wiphy->bands;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       if (bands[band]->channels[i].flags &
+                           IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       arg.n_channels++;
+               }
+       }
+
+       len = sizeof(struct wmi_channel_arg) * arg.n_channels;
+       arg.channels = kzalloc(len, GFP_KERNEL);
+       if (!arg.channels)
+               return -ENOMEM;
+
+       ch = arg.channels;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       channel = &bands[band]->channels[i];
+
+                       if (channel->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       ch->allow_ht   = true;
+
+                       /* FIXME: when should we really allow VHT? */
+                       ch->allow_vht = true;
+
+                       ch->allow_ibss =
+                               !(channel->flags & IEEE80211_CHAN_NO_IBSS);
+
+                       ch->ht40plus =
+                               !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
+
+                       passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
+                       ch->passive = passive;
+
+                       ch->freq = channel->center_freq;
+                       ch->min_power = channel->max_power * 3;
+                       ch->max_power = channel->max_power * 4;
+                       ch->max_reg_power = channel->max_reg_power * 4;
+                       ch->max_antenna_gain = channel->max_antenna_gain;
+                       ch->reg_class_id = 0; /* FIXME */
+
+                       /* FIXME: why use only legacy modes, why not any
+                        * HT/VHT modes? Would that even make any
+                        * difference? */
+                       if (channel->band == IEEE80211_BAND_2GHZ)
+                               ch->mode = MODE_11G;
+                       else
+                               ch->mode = MODE_11A;
+
+                       if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
+                               continue;
+
+                       ath10k_dbg(ATH10K_DBG_WMI,
+                                  "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+                                  __func__, ch - arg.channels, arg.n_channels,
+                                  ch->freq, ch->max_power, ch->max_reg_power,
+                                  ch->max_antenna_gain, ch->mode);
+
+                       ch++;
+               }
+       }
+
+       ret = ath10k_wmi_scan_chan_list(ar, &arg);
+       kfree(arg.channels);
+
+       return ret;
+}
+
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       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);
+
+       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,
+                                           regpair->regDmnEnum,
+                                           regpair->regDmnEnum, /* 2ghz */
+                                           regpair->regDmnEnum, /* 5ghz */
+                                           regpair->reg_2ghz_ctl,
+                                           regpair->reg_5ghz_ctl);
+       if (ret)
+               ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
+
+/***************/
+/* TX handlers */
+/***************/
+
+/*
+ * Frames sent to the FW have to be in "Native Wifi" format.
+ * Strip the QoS field from the 802.11 header.
+ */
+static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
+                                      struct ieee80211_tx_control *control,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       u8 *qos_ctl;
+
+       if (!ieee80211_is_data_qos(hdr->frame_control))
+               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);
+}
+
+static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       int ret;
+
+       /* TODO AP mode should be implemented */
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (!ieee80211_has_protected(hdr->frame_control))
+               return;
+
+       if (!key)
+               return;
+
+       if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+           key->cipher != WLAN_CIPHER_SUITE_WEP104)
+               return;
+
+       if (key->keyidx == arvif->def_wep_key_index)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_DEF_KEYID,
+                                       key->keyidx);
+       if (ret) {
+               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+               return;
+       }
+
+       arvif->def_wep_key_index = key->keyidx;
+}
+
+static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       /* This is case only for P2P_GO */
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
+           arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
+               spin_lock_bh(&ar->data_lock);
+               if (arvif->u.ap.noa_data)
+                       if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
+                                             GFP_ATOMIC))
+                               memcpy(skb_put(skb, arvif->u.ap.noa_len),
+                                      arvif->u.ap.noa_data,
+                                      arvif->u.ap.noa_len);
+               spin_unlock_bh(&ar->data_lock);
+       }
+}
+
+static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int ret;
+
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               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);
+       else
+               ret = ath10k_htt_tx(ar->htt, skb);
+
+       if (ret) {
+               ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_purge(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
+       struct ath10k_peer *peer;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+       const u8 *peer_addr;
+       int vdev_id;
+       int ret;
+
+       /* FW requirement: We must create a peer before FW will send out
+        * an offchannel frame. Otherwise the frame will be stuck and
+        * never transmitted. We delete the peer upon tx completion.
+        * It is unlikely that a peer for offchannel tx will already be
+        * present. However it may be in some rare cases so account for that.
+        * Otherwise we might remove a legitimate peer and break stuff. */
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               mutex_lock(&ar->conf_mutex);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+                          skb);
+
+               hdr = (struct ieee80211_hdr *)skb->data;
+               peer_addr = ieee80211_get_DA(hdr);
+               vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+
+               spin_lock_bh(&ar->data_lock);
+               peer = ath10k_peer_find(ar, vdev_id, peer_addr);
+               spin_unlock_bh(&ar->data_lock);
+
+               if (peer)
+                       ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
+                                  peer_addr, vdev_id);
+
+               if (!peer) {
+                       ret = ath10k_peer_create(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               spin_lock_bh(&ar->data_lock);
+               INIT_COMPLETION(ar->offchan_tx_completed);
+               ar->offchan_tx_skb = skb;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_tx_htt(ar, skb);
+
+               ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
+                                                 3 * HZ);
+               if (ret <= 0)
+                       ath10k_warn("timed out waiting for offchannel skb %p\n",
+                                   skb);
+
+               if (!peer) {
+                       ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               mutex_unlock(&ar->conf_mutex);
+       }
+}
+
+/************/
+/* Scanning */
+/************/
+
+/*
+ * This gets called if we dont get a heart-beat during scan.
+ * This may indicate the FW has hung and we need to abort the
+ * scan manually to prevent cancel_hw_scan() from deadlocking
+ */
+void ath10k_reset_scan(unsigned long ptr)
+{
+       struct ath10k *ar = (struct ath10k *)ptr;
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return;
+       }
+
+       ath10k_warn("scan timeout. resetting. fw issue?\n");
+
+       if (ar->scan.is_roc)
+               ieee80211_remain_on_channel_expired(ar->hw);
+       else
+               ieee80211_scan_completed(ar->hw, 1 /* aborted */);
+
+       ar->scan.in_progress = false;
+       complete_all(&ar->scan.completed);
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath10k_abort_scan(struct ath10k *ar)
+{
+       struct wmi_stop_scan_arg arg = {
+               .req_id = 1, /* FIXME */
+               .req_type = WMI_SCAN_STOP_ONE,
+               .u.scan_id = ATH10K_SCAN_ID,
+       };
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return 0;
+       }
+
+       ar->scan.aborting = true;
+       spin_unlock_bh(&ar->data_lock);
+
+       ret = ath10k_wmi_stop_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               return -EIO;
+       }
+
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+       if (ret == 0)
+               ath10k_warn("timed out while waiting for scan to stop\n");
+
+       /* scan completion may be done right after we timeout here, so let's
+        * check the in_progress and tell mac80211 scan is completed. if we
+        * don't do that and FW fails to send us scan completion indication
+        * then userspace won't be able to scan anymore */
+       ret = 0;
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               ath10k_warn("could not stop scan. its still in progress\n");
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               ret = -ETIMEDOUT;
+       }
+       spin_unlock_bh(&ar->data_lock);
+
+       return ret;
+}
+
+static int ath10k_start_scan(struct ath10k *ar,
+                            const struct wmi_start_scan_arg *arg)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_start_scan(ar, arg);
+       if (ret)
+               return ret;
+
+       /* make sure we submit the command so the completion
+       * timeout makes sense */
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+       if (ret == 0) {
+               ath10k_abort_scan(ar);
+               return ret;
+       }
+
+       /* the scan can complete earlier, before we even
+        * start the timer. in that case the timer handler
+        * checks ar->scan.in_progress and bails out if its
+        * false. Add a 200ms margin to account event/command
+        * processing. */
+       mod_timer(&ar->scan.timeout, jiffies +
+                 msecs_to_jiffies(arg->max_scan_time+200));
+       return 0;
+}
+
+/**********************/
+/* mac80211 callbacks */
+/**********************/
+
+static void ath10k_tx(struct ieee80211_hw *hw,
+                     struct ieee80211_tx_control *control,
+                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = NULL;
+       u32 vdev_id = 0;
+       u8 tid;
+
+       if (info->control.vif) {
+               arvif = ath10k_vif_to_arvif(info->control.vif);
+               vdev_id = arvif->vdev_id;
+       } else if (ar->monitor_enabled) {
+               vdev_id = ar->monitor_vdev_id;
+       }
+
+       /* We should disable CCK RATE due to P2P */
+       if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+               ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+       /* we must calculate tid before we apply qos workaround
+        * as we'd lose the qos control field */
+       tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+       if (ieee80211_is_data_qos(hdr->frame_control) &&
+           is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               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);
+
+       memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
+       ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+       ATH10K_SKB_CB(skb)->htt.tid = tid;
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               spin_lock_bh(&ar->data_lock);
+               ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+               ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
+
+               skb_queue_tail(&ar->offchan_tx_queue, skb);
+               ieee80211_queue_work(hw, &ar->offchan_tx_work);
+               return;
+       }
+
+       ath10k_tx_htt(ar, skb);
+}
+
+/*
+ * Initialize various parameters with default vaules.
+ */
+static int ath10k_start(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+       if (ret)
+               ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
+                           ret);
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+       if (ret)
+               ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
+                           ret);
+
+       return 0;
+}
+
+static void ath10k_stop(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       /* avoid leaks in case FW never confirms scan for offchannel */
+       cancel_work_sync(&ar->offchan_tx_work);
+       ath10k_offchan_tx_purge(ar);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
+       u32 flags;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+                          conf->chandef.chan->center_freq);
+               spin_lock_bh(&ar->data_lock);
+               ar->rx_channel = conf->chandef.chan;
+               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_MONITOR) {
+               if (conf->flags & IEEE80211_CONF_MONITOR)
+                       ret = ath10k_monitor_create(ar);
+               else
+                       ret = ath10k_monitor_destroy(ar);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+/*
+ * TODO:
+ * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
+ * because we will send mgmt frames without CCK. This requirement
+ * for P2P_FIND/GO_NEG should be handled by checking CCK flag
+ * in the TX packet.
+ */
+static int ath10k_add_interface(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       int ret = 0;
+       u32 value;
+       int bit;
+
+       mutex_lock(&ar->conf_mutex);
+
+       arvif->ar = ar;
+       arvif->vif = vif;
+
+       if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
+               ath10k_warn("Only one monitor interface allowed\n");
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       arvif->vdev_id = bit - 1;
+       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+       ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+
+       if (ar->p2p)
+               arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_STATION:
+               arvif->vdev_type = WMI_VDEV_TYPE_STA;
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
+               break;
+       case NL80211_IFTYPE_AP:
+               arvif->vdev_type = WMI_VDEV_TYPE_AP;
+
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+                  arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+
+       ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
+                                    arvif->vdev_subtype, vif->addr);
+       if (ret) {
+               ath10k_warn("WMI vdev create failed: ret %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
+                                       arvif->def_wep_key_index);
+       if (ret)
+               ath10k_warn("Failed to set default keyid: %d\n", ret);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+                                       ATH10K_HW_TXRX_NATIVE_WIFI);
+       if (ret)
+               ath10k_warn("Failed to set TX encap: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
+               if (ret) {
+                       ath10k_warn("Failed to create peer for AP: %d\n", ret);
+                       goto exit;
+               }
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+               param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+               value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+               value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = true;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_remove_interface(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+
+       ar->free_vdev_map |= 1 << (arvif->vdev_id);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+               if (ret)
+                       ath10k_warn("Failed to remove peer for AP: %d\n", ret);
+
+               kfree(arvif->u.ap.noa_data);
+       }
+
+       ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_warn("WMI vdev delete failed: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = false;
+
+       ath10k_peer_cleanup(ar, arvif->vdev_id);
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+/*
+ * FIXME: Has to be verified.
+ */
+#define SUPPORTED_FILTERS                      \
+       (FIF_PROMISC_IN_BSS |                   \
+       FIF_ALLMULTI |                          \
+       FIF_CONTROL |                           \
+       FIF_PSPOLL |                            \
+       FIF_OTHER_BSS |                         \
+       FIF_BCN_PRBRESP_PROMISC |               \
+       FIF_PROBE_REQ |                         \
+       FIF_FCSFAIL)
+
+static void ath10k_configure_filter(struct ieee80211_hw *hw,
+                                   unsigned int changed_flags,
+                                   unsigned int *total_flags,
+                                   u64 multicast)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       changed_flags &= SUPPORTED_FILTERS;
+       *total_flags &= SUPPORTED_FILTERS;
+       ar->filter_flags = *total_flags;
+
+       if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+           !ar->monitor_enabled) {
+               ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+               if (ret)
+                       ath10k_warn("Unable to start monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
+       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+                  ar->monitor_enabled) {
+               ret = ath10k_monitor_stop(ar);
+               if (ret)
+                       ath10k_warn("Unable to stop monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_bss_conf *info,
+                                   u32 changed)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & BSS_CHANGED_IBSS)
+               ath10k_control_ibss(arvif, info, vif->addr);
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               arvif->beacon_interval = info->beacon_int;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_BEACON_INTERVAL,
+                                               arvif->beacon_interval);
+               if (ret)
+                       ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Beacon interval: %d set for VDEV: %d\n",
+                                  arvif->beacon_interval, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON) {
+               ret = ath10k_wmi_pdev_set_param(ar,
+                                               WMI_PDEV_PARAM_BEACON_TX_MODE,
+                                               WMI_BEACON_STAGGERED_MODE);
+               if (ret)
+                       ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set staggered beacon mode for VDEV: %d\n",
+                                  arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INFO) {
+               arvif->dtim_period = info->dtim_period;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_DTIM_PERIOD,
+                                               arvif->dtim_period);
+               if (ret)
+                       ath10k_warn("Failed to set dtim period for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set dtim period: %d for VDEV: %d\n",
+                                  arvif->dtim_period, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_SSID &&
+           vif->type == NL80211_IFTYPE_AP) {
+               arvif->u.ap.ssid_len = info->ssid_len;
+               if (info->ssid_len)
+                       memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len);
+               arvif->u.ap.hidden_ssid = info->hidden_ssid;
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(info->bssid)) {
+                       ret = ath10k_peer_create(ar, arvif->vdev_id,
+                                                info->bssid);
+                       if (ret)
+                               ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                           info->bssid, arvif->vdev_id);
+                       else
+                               ath10k_dbg(ATH10K_DBG_MAC,
+                                          "Added peer: %pM for VDEV: %d\n",
+                                          info->bssid, arvif->vdev_id);
+
+
+                       if (vif->type == NL80211_IFTYPE_STATION) {
+                               /*
+                                * this is never erased as we it for crypto key
+                                * clearing; this is FW requirement
+                                */
+                               memcpy(arvif->u.sta.bssid, info->bssid,
+                                      ETH_ALEN);
+
+                               ret = ath10k_vdev_start(arvif);
+                               if (!ret)
+                                       ath10k_dbg(ATH10K_DBG_MAC,
+                                                  "VDEV: %d started with BSSID: %pM\n",
+                                                  arvif->vdev_id, info->bssid);
+                       }
+
+                       /*
+                        * Mac80211 does not keep IBSS bssid when leaving IBSS,
+                        * so driver need to store it. It is needed when leaving
+                        * IBSS in order to remove BSSID peer.
+                        */
+                       if (vif->type == NL80211_IFTYPE_ADHOC)
+                               memcpy(arvif->u.ibss.bssid, info->bssid,
+                                      ETH_ALEN);
+               }
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED)
+               ath10k_control_beaconing(arvif, info);
+
+       if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+               u32 cts_prot;
+               if (info->use_cts_prot)
+                       cts_prot = 1;
+               else
+                       cts_prot = 0;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_ENABLE_RTSCTS,
+                                               cts_prot);
+               if (ret)
+                       ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set CTS prot: %d for VDEV: %d\n",
+                                  cts_prot, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               u32 slottime;
+               if (info->use_short_slot)
+                       slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
+
+               else
+                       slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_SLOT_TIME,
+                                               slottime);
+               if (ret)
+                       ath10k_warn("Failed to set erp slot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set slottime: %d for VDEV: %d\n",
+                                  slottime, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               u32 preamble;
+               if (info->use_short_preamble)
+                       preamble = WMI_VDEV_PREAMBLE_SHORT;
+               else
+                       preamble = WMI_VDEV_PREAMBLE_LONG;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_PREAMBLE,
+                                               preamble);
+               if (ret)
+                       ath10k_warn("Failed to set preamble for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set preamble: %d for VDEV: %d\n",
+                                  preamble, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (info->assoc)
+                       ath10k_bss_assoc(hw, vif, info);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_hw_scan(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         struct cfg80211_scan_request *req)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = false;
+       ar->scan.vdev_id = arvif->vdev_id;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+
+       if (!req->no_cck)
+               arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES;
+
+       if (req->ie_len) {
+               arg.ie_len = req->ie_len;
+               memcpy(arg.ie, req->ie, arg.ie_len);
+       }
+
+       if (req->n_ssids) {
+               arg.n_ssids = req->n_ssids;
+               for (i = 0; i < arg.n_ssids; i++) {
+                       arg.ssids[i].len  = req->ssids[i].ssid_len;
+                       arg.ssids[i].ssid = req->ssids[i].ssid;
+               }
+       }
+
+       if (req->n_channels) {
+               arg.n_channels = req->n_channels;
+               for (i = 0; i < arg.n_channels; i++)
+                       arg.channels[i] = req->channels[i]->center_freq;
+       }
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start hw scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+       ret = ath10k_abort_scan(ar);
+       if (ret) {
+               ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
+                           ret);
+               ieee80211_scan_completed(hw, 1 /* aborted */);
+       }
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_peer *peer;
+       const u8 *peer_addr;
+       bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                     key->cipher == WLAN_CIPHER_SUITE_WEP104;
+       int ret = 0;
+
+       if (key->keyidx > WMI_MAX_KEY_INDEX)
+               return -ENOSPC;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (sta)
+               peer_addr = sta->addr;
+       else if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               peer_addr = vif->bss_conf.bssid;
+       else
+               peer_addr = vif->addr;
+
+       key->hw_key_idx = key->keyidx;
+
+       /* the peer should not disappear in mid-way (unless FW goes awry) since
+        * we already hold conf_mutex. we just make sure its there now. */
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer) {
+               if (cmd == SET_KEY) {
+                       ath10k_warn("cannot install key for non-existent peer %pM\n",
+                                   peer_addr);
+                       ret = -EOPNOTSUPP;
+                       goto exit;
+               } else {
+                       /* if the peer doesn't exist there is no key to disable
+                        * anymore */
+                       goto exit;
+               }
+       }
+
+       if (is_wep) {
+               if (cmd == SET_KEY)
+                       arvif->wep_keys[key->keyidx] = key;
+               else
+                       arvif->wep_keys[key->keyidx] = NULL;
+
+               if (cmd == DISABLE_KEY)
+                       ath10k_clear_vdev_key(arvif, key);
+       }
+
+       ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+       if (ret) {
+               ath10k_warn("ath10k_install_key failed (%d)\n", ret);
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       if (peer && cmd == SET_KEY)
+               peer->keys[key->keyidx] = key;
+       else if (peer && cmd == DISABLE_KEY)
+               peer->keys[key->keyidx] = NULL;
+       else if (peer == NULL)
+               /* impossible unless FW goes crazy */
+               ath10k_warn("peer %pM disappeared!\n", peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_sta_state(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           enum ieee80211_sta_state old_state,
+                           enum ieee80211_sta_state new_state)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE &&
+           vif->type != NL80211_IFTYPE_STATION) {
+               /*
+                * New station addition.
+                */
+               ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Added peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+       } else if ((old_state == IEEE80211_STA_NONE &&
+                   new_state == IEEE80211_STA_NOTEXIST)) {
+               /*
+                * Existing station deletion.
+                */
+               ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Removed peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+
+               if (vif->type == NL80211_IFTYPE_STATION)
+                       ath10k_bss_disassoc(hw, vif);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * New association.
+                */
+               ret = ath10k_station_assoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to associate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to assoc state\n",
+                                  sta->addr);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * Disassociation.
+                */
+               ret = ath10k_station_disassoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to disassociate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to disassociated state\n",
+                                  sta->addr);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
+                                u16 ac, bool enable)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 value = 0;
+       int ret = 0;
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+               return 0;
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_VI:
+               value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BE:
+               value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BK:
+               value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+               break;
+       }
+
+       if (enable)
+               arvif->u.sta.uapsd |= value;
+       else
+               arvif->u.sta.uapsd &= ~value;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_UAPSD,
+                                         arvif->u.sta.uapsd);
+       if (ret) {
+               ath10k_warn("could not set uapsd params %d\n", ret);
+               goto exit;
+       }
+
+       if (arvif->u.sta.uapsd)
+               value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD;
+       else
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_RX_WAKE_POLICY,
+                                         value);
+       if (ret)
+               ath10k_warn("could not set rx wake param %d\n", ret);
+
+exit:
+       return ret;
+}
+
+static int ath10k_conf_tx(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif, u16 ac,
+                         const struct ieee80211_tx_queue_params *params)
+{
+       struct ath10k *ar = hw->priv;
+       struct wmi_wmm_params_arg *p = NULL;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               p = &ar->wmm_params.ac_vo;
+               break;
+       case IEEE80211_AC_VI:
+               p = &ar->wmm_params.ac_vi;
+               break;
+       case IEEE80211_AC_BE:
+               p = &ar->wmm_params.ac_be;
+               break;
+       case IEEE80211_AC_BK:
+               p = &ar->wmm_params.ac_bk;
+               break;
+       }
+
+       if (WARN_ON(!p)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       p->cwmin = params->cw_min;
+       p->cwmax = params->cw_max;
+       p->aifs = params->aifs;
+
+       /*
+        * The channel time duration programmed in the HW is in absolute
+        * microseconds, while mac80211 gives the txop in units of
+        * 32 microseconds.
+        */
+       p->txop = params->txop * 32;
+
+       /* FIXME: FW accepts wmm params per hw, not per vif */
+       ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
+       if (ret) {
+               ath10k_warn("could not set wmm params %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
+       if (ret)
+               ath10k_warn("could not set sta uapsd %d\n", ret);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+#define ATH10K_ROC_TIMEOUT_HZ (2*HZ)
+
+static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_channel *chan,
+                                   int duration,
+                                   enum ieee80211_roc_type type)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       INIT_COMPLETION(ar->scan.on_channel);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = true;
+       ar->scan.vdev_id = arvif->vdev_id;
+       ar->scan.roc_freq = chan->center_freq;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+       arg.n_channels = 1;
+       arg.channels[0] = chan->center_freq;
+       arg.dwell_time_active = duration;
+       arg.dwell_time_passive = duration;
+       arg.max_scan_time = 2 * duration;
+       arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
+       arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start roc scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+               goto exit;
+       }
+
+       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+       if (ret == 0) {
+               ath10k_warn("could not switch to channel for roc scan\n");
+               ath10k_abort_scan(ar);
+               ret = -ETIMEDOUT;
+               goto exit;
+       }
+
+       ret = 0;
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+       ath10k_abort_scan(ar);
+       mutex_unlock(&ar->conf_mutex);
+
+       return 0;
+}
+
+/*
+ * Both RTS and Fragmentation threshold are interface-specific
+ * in ath10k, but device-specific in mac80211.
+ */
+static void ath10k_set_rts_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 rts = ar_iter->ar->hw->wiphy->rts_threshold;
+
+       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+
+       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                                rts);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set RTS threshold: %d for VDEV: %d\n",
+                          rts, arvif->vdev_id);
+}
+
+static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       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);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+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);
+
+       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                       frag);
+
+       ar_iter->ret = ret;
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set frag threshold: %d for VDEV: %d\n",
+                          frag, arvif->vdev_id);
+}
+
+static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       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);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       /* mac80211 doesn't care if we really xmit queued frames or not
+        * we'll collect those frames either way if we stop/delete vdevs */
+       if (drop)
+               return;
+
+       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);
+               }), ATH10K_FLUSH_TIMEOUT_HZ);
+       if (ret <= 0)
+               ath10k_warn("tx not flushed\n");
+}
+
+/* TODO: Implement this function properly
+ * For now it is needed to reply to Probe Requests in IBSS mode.
+ * Propably we need this information from FW.
+ */
+static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
+{
+       return 1;
+}
+
+static const struct ieee80211_ops ath10k_ops = {
+       .tx                             = ath10k_tx,
+       .start                          = ath10k_start,
+       .stop                           = ath10k_stop,
+       .config                         = ath10k_config,
+       .add_interface                  = ath10k_add_interface,
+       .remove_interface               = ath10k_remove_interface,
+       .configure_filter               = ath10k_configure_filter,
+       .bss_info_changed               = ath10k_bss_info_changed,
+       .hw_scan                        = ath10k_hw_scan,
+       .cancel_hw_scan                 = ath10k_cancel_hw_scan,
+       .set_key                        = ath10k_set_key,
+       .sta_state                      = ath10k_sta_state,
+       .conf_tx                        = ath10k_conf_tx,
+       .remain_on_channel              = ath10k_remain_on_channel,
+       .cancel_remain_on_channel       = ath10k_cancel_remain_on_channel,
+       .set_rts_threshold              = ath10k_set_rts_threshold,
+       .set_frag_threshold             = ath10k_set_frag_threshold,
+       .flush                          = ath10k_flush,
+       .tx_last_beacon                 = ath10k_tx_last_beacon,
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+       .bitrate                = (_rate), \
+       .flags                  = (_flags), \
+       .hw_value               = (_rateid), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_2GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+#define CHAN5G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_5GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+static const struct ieee80211_channel ath10k_2ghz_channels[] = {
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
+};
+
+static const struct ieee80211_channel ath10k_5ghz_channels[] = {
+       CHAN5G(36, 5180, 14),
+       CHAN5G(40, 5200, 15),
+       CHAN5G(44, 5220, 16),
+       CHAN5G(48, 5240, 17),
+       CHAN5G(52, 5260, 18),
+       CHAN5G(56, 5280, 19),
+       CHAN5G(60, 5300, 20),
+       CHAN5G(64, 5320, 21),
+       CHAN5G(100, 5500, 22),
+       CHAN5G(104, 5520, 23),
+       CHAN5G(108, 5540, 24),
+       CHAN5G(112, 5560, 25),
+       CHAN5G(116, 5580, 26),
+       CHAN5G(120, 5600, 27),
+       CHAN5G(124, 5620, 28),
+       CHAN5G(128, 5640, 29),
+       CHAN5G(132, 5660, 30),
+       CHAN5G(136, 5680, 31),
+       CHAN5G(140, 5700, 32),
+       CHAN5G(149, 5745, 33),
+       CHAN5G(153, 5765, 34),
+       CHAN5G(157, 5785, 35),
+       CHAN5G(161, 5805, 36),
+       CHAN5G(165, 5825, 37),
+};
+
+static struct ieee80211_rate ath10k_rates[] = {
+       /* CCK */
+       RATETAB_ENT(10,  0x82, 0),
+       RATETAB_ENT(20,  0x84, 0),
+       RATETAB_ENT(55,  0x8b, 0),
+       RATETAB_ENT(110, 0x96, 0),
+       /* OFDM */
+       RATETAB_ENT(60,  0x0c, 0),
+       RATETAB_ENT(90,  0x12, 0),
+       RATETAB_ENT(120, 0x18, 0),
+       RATETAB_ENT(180, 0x24, 0),
+       RATETAB_ENT(240, 0x30, 0),
+       RATETAB_ENT(360, 0x48, 0),
+       RATETAB_ENT(480, 0x60, 0),
+       RATETAB_ENT(540, 0x6c, 0),
+};
+
+#define ath10k_a_rates (ath10k_rates + 4)
+#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4)
+#define ath10k_g_rates (ath10k_rates + 0)
+#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
+
+struct ath10k *ath10k_mac_create(void)
+{
+       struct ieee80211_hw *hw;
+       struct ath10k *ar;
+
+       hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops);
+       if (!hw)
+               return NULL;
+
+       ar = hw->priv;
+       ar->hw = hw;
+
+       return ar;
+}
+
+void ath10k_mac_destroy(struct ath10k *ar)
+{
+       ieee80211_free_hw(ar->hw);
+}
+
+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)
+       }
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb = {
+       .limits = ath10k_if_limits,
+       .n_limits = ARRAY_SIZE(ath10k_if_limits),
+       .max_interfaces = 8,
+       .num_different_channels = 1,
+       .beacon_int_infra_match = true,
+};
+
+static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
+{
+       struct ieee80211_sta_vht_cap vht_cap = {0};
+       u16 mcs_map;
+
+       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;
+
+       vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+       vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+
+       return vht_cap;
+}
+
+static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
+{
+       int i;
+       struct ieee80211_sta_ht_cap ht_cap = {0};
+
+       if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
+               return ht_cap;
+
+       ht_cap.ht_supported = 1;
+       ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+       ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
+               u32 smps;
+
+               smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
+               smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               ht_cap.cap |= smps;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
+               ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
+               u32 stbc;
+
+               stbc   = ar->ht_cap_info;
+               stbc  &= WMI_HT_CAP_RX_STBC;
+               stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
+               stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc  &= IEEE80211_HT_CAP_RX_STBC;
+
+               ht_cap.cap |= stbc;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
+               ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
+               ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
+
+       /* max AMSDU is implicitly taken from vht_cap_info */
+       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++)
+               ht_cap.mcs.rx_mask[i] = 0xFF;
+
+       ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+       return ht_cap;
+}
+
+
+static void ath10k_get_arvif_iter(void *data, u8 *mac,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k_vif_iter *arvif_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (arvif->vdev_id == arvif_iter->vdev_id)
+               arvif_iter->arvif = arvif;
+}
+
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
+{
+       struct ath10k_vif_iter arvif_iter;
+       u32 flags;
+
+       memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter));
+       arvif_iter.vdev_id = vdev_id;
+
+       flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+       ieee80211_iterate_active_interfaces_atomic(ar->hw,
+                                                  flags,
+                                                  ath10k_get_arvif_iter,
+                                                  &arvif_iter);
+       if (!arvif_iter.arvif) {
+               ath10k_warn("No VIF found for VDEV: %d\n", vdev_id);
+               return NULL;
+       }
+
+       return arvif_iter.arvif;
+}
+
+int ath10k_mac_register(struct ath10k *ar)
+{
+       struct ieee80211_supported_band *band;
+       struct ieee80211_sta_vht_cap vht_cap;
+       struct ieee80211_sta_ht_cap ht_cap;
+       void *channels;
+       int ret;
+
+       SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
+
+       SET_IEEE80211_DEV(ar->hw, ar->dev);
+
+       ht_cap = ath10k_get_ht_cap(ar);
+       vht_cap = ath10k_create_vht_cap(ar);
+
+       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+               channels = kmemdup(ath10k_2ghz_channels,
+                                  sizeof(ath10k_2ghz_channels),
+                                  GFP_KERNEL);
+               if (!channels)
+                       return -ENOMEM;
+
+               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_g_rates_size;
+               band->bitrates = ath10k_g_rates;
+               band->ht_cap = ht_cap;
+
+               /* vht is not supported in 2.4 GHz */
+
+               ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+       }
+
+       if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
+               channels = kmemdup(ath10k_5ghz_channels,
+                                  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;
+               }
+
+               band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_a_rates_size;
+               band->bitrates = ath10k_a_rates;
+               band->ht_cap = ht_cap;
+               band->vht_cap = vht_cap;
+               ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+       }
+
+       ar->hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               BIT(NL80211_IFTYPE_P2P_GO);
+
+       ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                       IEEE80211_HW_SUPPORTS_PS |
+                       IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                       IEEE80211_HW_SUPPORTS_UAPSD |
+                       IEEE80211_HW_MFP_CAPABLE |
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_HAS_RATE_CONTROL |
+                       IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                       IEEE80211_HW_WANT_MONITOR_VIF |
+                       IEEE80211_HW_AP_LINK_PS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+               ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
+               ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+               ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
+       }
+
+       ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
+       ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
+
+       ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+
+       ar->hw->channel_change_time = 5000;
+       ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       ar->hw->wiphy->max_remain_on_channel_duration = 5000;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+       /*
+        * on LL hardware queues are managed entirely by the FW
+        * so we only advertise to mac we can do the queues thing
+        */
+       ar->hw->queues = 4;
+
+       ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
+       ar->hw->wiphy->n_iface_combinations = 1;
+
+       ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
+                           ath10k_reg_notifier);
+       if (ret) {
+               ath10k_err("Regulatory initialization failed\n");
+               return ret;
+       }
+
+       ret = ieee80211_register_hw(ar->hw);
+       if (ret) {
+               ath10k_err("ieee80211 registration failed: %d\n", ret);
+               return ret;
+       }
+
+       if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
+               ret = regulatory_hint(ar->hw->wiphy,
+                                     ar->ath_common.regulatory.alpha2);
+               if (ret)
+                       goto exit;
+       }
+
+       return 0;
+exit:
+       ieee80211_unregister_hw(ar->hw);
+       return ret;
+}
+
+void ath10k_mac_unregister(struct ath10k *ar)
+{
+       ieee80211_unregister_hw(ar->hw);
+
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
+       SET_IEEE80211_DEV(ar->hw, NULL);
+}
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
new file mode 100644 (file)
index 0000000..27fc92e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MAC_H_
+#define _MAC_H_
+
+#include <net/mac80211.h>
+#include "core.h"
+
+struct ath10k_generic_iter {
+       struct ath10k *ar;
+       int ret;
+};
+
+struct ath10k *ath10k_mac_create(void);
+void ath10k_mac_destroy(struct ath10k *ar);
+int ath10k_mac_register(struct ath10k *ar);
+void ath10k_mac_unregister(struct ath10k *ar);
+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);
+
+static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
+{
+       return (struct ath10k_vif *)vif->drv_priv;
+}
+
+static inline void ath10k_tx_h_seq_no(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (info->flags  & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+               if (arvif->tx_seq_no == 0)
+                       arvif->tx_seq_no = 0x1000;
+
+               if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+                       arvif->tx_seq_no += 0x10;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no);
+       }
+}
+
+#endif /* _MAC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
new file mode 100644 (file)
index 0000000..c8e9056
--- /dev/null
@@ -0,0 +1,2506 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "core.h"
+#include "debug.h"
+
+#include "targaddrs.h"
+#include "bmi.h"
+
+#include "hif.h"
+#include "htc.h"
+
+#include "ce.h"
+#include "pci.h"
+
+unsigned int ath10k_target_ps;
+module_param(ath10k_target_ps, uint, 0644);
+MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+
+#define QCA988X_1_0_DEVICE_ID  (0xabcd)
+#define QCA988X_2_0_DEVICE_ID  (0x003c)
+
+static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
+       { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
+       { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
+       {0}
+};
+
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data);
+
+static void ath10k_pci_process_ce(struct ath10k *ar);
+static int ath10k_pci_post_rx(struct ath10k *ar);
+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 const struct ce_attr host_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+       /* could be moved to share CE3 */
+       /* target->host HTT + HTC control */
+       { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
+       /* target->host WMI */
+       { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+       /* host->target WMI */
+       { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+       /* host->target HTT */
+       { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
+                   CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+       /* unused */
+       { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* Target autonomous hif_memcpy */
+       { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* ce_diag, the Diagnostic Window */
+       { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+/* Target firmware's Copy Engine configuration. */
+static const struct ce_pipe_config target_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
+       /* target->host HTT + HTC control */
+       { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
+       /* target->host WMI */
+       { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target WMI */
+       { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target HTT */
+       { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+       /* NB: 50% of src nentries, since tx has 2 frags */
+       /* unused */
+       { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* Reserved for target autonomous hif_memcpy */
+       { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+       /* CE7 used only by Host */
+};
+
+/*
+ * Diagnostic read/write access is provided for startup/config/debug usage.
+ * Caller must guarantee proper alignment, when applicable, and single user
+ * at any moment.
+ */
+static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
+                                   int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       /* Host buffer address in CE space */
+       u32 ce_data;
+       dma_addr_t ce_data_base = 0;
+       void *data_buf = NULL;
+       int i;
+
+       /*
+        * This code cannot handle reads to non-memory space. Redirect to the
+        * register read fn but preserve the multi word read capability of
+        * this fn
+        */
+       if (address < DRAM_BASE_ADDRESS) {
+               if (!IS_ALIGNED(address, 4) ||
+                   !IS_ALIGNED((unsigned long)data, 4))
+                       return -EIO;
+
+               while ((nbytes >= 4) &&  ((ret = ath10k_pci_diag_read_access(
+                                          ar, address, (u32 *)data)) == 0)) {
+                       nbytes -= sizeof(u32);
+                       address += sizeof(u32);
+                       data += sizeof(u32);
+               }
+               return ret;
+       }
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed from Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+       memset(data_buf, 0, orig_nbytes);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               nbytes = min_t(unsigned int, remaining_bytes,
+                              DIAG_TRANSFER_LIMIT);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data);
+               if (ret != 0)
+                       goto done;
+
+               /* Request CE to send from Target(!) address to Host buffer */
+               /*
+                * The address supplied by the caller is in the
+                * Target CPU virtual address space.
+                *
+                * In order to use this address with the diagnostic CE,
+                * convert it from Target CPU virtual address space
+                * to CE address space
+                */
+               ath10k_pci_wake(ar);
+               address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
+                                                    address);
+               ath10k_pci_sleep(ar);
+
+               ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
+                                0);
+               if (ret)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != (u32) address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (ret == 0) {
+               /* Copy data from allocated DMA buf to caller's buf */
+               WARN_ON_ONCE(orig_nbytes & 3);
+               for (i = 0; i < orig_nbytes / sizeof(__le32); i++) {
+                       ((u32 *)data)[i] =
+                               __le32_to_cpu(((__le32 *)data_buf)[i]);
+               }
+       } else
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
+                          __func__, address);
+
+       if (data_buf)
+               pci_free_consistent(ar_pci->pdev, orig_nbytes,
+                                   data_buf, ce_data_base);
+
+       return ret;
+}
+
+/* Read 4-byte aligned data from Target memory or register */
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       *data = ath10k_pci_read32(ar, address);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+                                    const void *data, int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       void *data_buf = NULL;
+       u32 ce_data;    /* Host buffer address in CE space */
+       dma_addr_t ce_data_base = 0;
+       int i;
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed to Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       /* Copy caller's data to allocated DMA buf */
+       WARN_ON_ONCE(orig_nbytes & 3);
+       for (i = 0; i < orig_nbytes / sizeof(__le32); i++)
+               ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]);
+
+       /*
+        * The address supplied by the caller is in the
+        * Target CPU virtual address space.
+        *
+        * In order to use this address with the diagnostic CE,
+        * convert it from
+        *    Target CPU virtual address space
+        * to
+        *    CE address space
+        */
+       ath10k_pci_wake(ar);
+       address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address);
+       ath10k_pci_sleep(ar);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               /* FIXME: check cast */
+               nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+               /* Set up to receive directly into Target(!) address */
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address);
+               if (ret != 0)
+                       goto done;
+
+               /*
+                * Request CE to send caller-supplied data that
+                * was copied to bounce buffer to Target(!) address.
+                */
+               ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data,
+                                    nbytes, 0, 0);
+               if (ret != 0)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (data_buf) {
+               pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
+                                   ce_data_base);
+       }
+
+       if (ret != 0)
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
+                          address);
+
+       return ret;
+}
+
+/* Write 4B data to Target memory or register */
+static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address,
+                                       u32 data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_write_mem(ar, address, &data,
+                                                sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       ath10k_pci_write32(ar, address, data);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static bool ath10k_pci_target_is_awake(struct ath10k *ar)
+{
+       void __iomem *mem = ath10k_pci_priv(ar)->mem;
+       u32 val;
+       val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS +
+                      RTC_STATE_ADDRESS);
+       return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
+}
+
+static void ath10k_pci_wait(struct ath10k *ar)
+{
+       int n = 100;
+
+       while (n-- && !ath10k_pci_target_is_awake(ar))
+               msleep(10);
+
+       if (n < 0)
+               ath10k_warn("Unable to wakeup target\n");
+}
+
+void ath10k_do_pci_wake(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+       int tot_delay = 0;
+       int curr_delay = 5;
+
+       if (atomic_read(&ar_pci->keep_awake_count) == 0) {
+               /* Force AWAKE */
+               iowrite32(PCIE_SOC_WAKE_V_MASK,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+       atomic_inc(&ar_pci->keep_awake_count);
+
+       if (ar_pci->verified_awake)
+               return;
+
+       for (;;) {
+               if (ath10k_pci_target_is_awake(ar)) {
+                       ar_pci->verified_awake = true;
+                       break;
+               }
+
+               if (tot_delay > PCIE_WAKE_TIMEOUT) {
+                       ath10k_warn("target takes too long to wake up (awake count %d)\n",
+                                   atomic_read(&ar_pci->keep_awake_count));
+                       break;
+               }
+
+               udelay(curr_delay);
+               tot_delay += curr_delay;
+
+               if (curr_delay < 50)
+                       curr_delay += 5;
+       }
+}
+
+void ath10k_do_pci_sleep(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+
+       if (atomic_dec_and_test(&ar_pci->keep_awake_count)) {
+               /* Allow sleep */
+               ar_pci->verified_awake = false;
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+}
+
+/*
+ * FIXME: Handle OOM properly.
+ */
+static inline
+struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k_pci_compl *compl = NULL;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (list_empty(&pipe_info->compl_free)) {
+               ath10k_warn("Completion buffers are full\n");
+               goto exit;
+       }
+       compl = list_first_entry(&pipe_info->compl_free,
+                                struct ath10k_pci_compl, list);
+       list_del(&compl->list);
+exit:
+       spin_unlock_bh(&pipe_info->pipe_lock);
+       return compl;
+}
+
+/* Called by lower (CE) layer when a send to Target completes. */
+static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
+                                   void *transfer_context,
+                                   u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       bool process = false;
+
+       do {
+               /*
+                * For the send completion of an item in sendlist, just
+                * increment num_sends_allowed. The upper layer callback will
+                * be triggered when last fragment is done with send.
+                */
+               if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
+                       spin_lock_bh(&pipe_info->pipe_lock);
+                       pipe_info->num_sends_allowed++;
+                       spin_unlock_bh(&pipe_info->pipe_lock);
+                       continue;
+               }
+
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_SEND;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = 0;
+
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               process = true;
+       } while (ath10k_ce_completed_send_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id) == 0);
+
+       /*
+        * If only some of the items within a sendlist have completed,
+        * don't invoke completion processing until the entire sendlist
+        * has been sent.
+        */
+       if (!process)
+               return;
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Called by lower (CE) layer when data is received from the Target. */
+static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
+                                   void *transfer_context, u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id,
+                                   unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+
+       do {
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_RECV;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = flags;
+
+               skb = transfer_context;
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+       } while (ath10k_ce_completed_recv_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id,
+                                                          &flags) == 0);
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Send the first nbytes bytes of the buffer */
+static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                   unsigned int transfer_id,
+                                   unsigned int bytes, struct sk_buff *nbuf)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
+       struct ce_state *ce_hdl = pipe_info->ce_hdl;
+       struct ce_sendlist sendlist;
+       unsigned int len;
+       u32 flags = 0;
+       int ret;
+
+       memset(&sendlist, 0, sizeof(struct ce_sendlist));
+
+       len = min(bytes, nbuf->len);
+       bytes -= len;
+
+       if (len & 3)
+               ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+
+       ath10k_dbg(ATH10K_DBG_PCI,
+                  "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
+                  nbuf->data, (unsigned long long) skb_cb->paddr,
+                  nbuf->len, len);
+       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                       "ath10k tx: data: ",
+                       nbuf->data, nbuf->len);
+
+       ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
+
+       /* Make sure we have resources to handle this request */
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (!pipe_info->num_sends_allowed) {
+               ath10k_warn("Pipe: %d is full\n", pipe_id);
+               spin_unlock_bh(&pipe_info->pipe_lock);
+               return -ENOSR;
+       }
+       pipe_info->num_sends_allowed--;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+       if (ret)
+               ath10k_warn("CE send failed: %p\n", nbuf);
+
+       return ret;
+}
+
+static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
+       int ret;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       ret = pipe_info->num_sends_allowed;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       return ret;
+}
+
+static void ath10k_pci_hif_dump_area(struct ath10k *ar)
+{
+       u32 reg_dump_area = 0;
+       u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
+       u32 host_addr;
+       int ret;
+       u32 i;
+
+       ath10k_err("firmware crashed!\n");
+       ath10k_err("hardware name %s version 0x%x\n",
+                  ar->hw_params.name, ar->target_version);
+       ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
+                  ar->fw_version_minor, ar->fw_version_release,
+                  ar->fw_version_build);
+
+       host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
+       if (ath10k_pci_diag_read_mem(ar, host_addr,
+                                    &reg_dump_area, sizeof(u32)) != 0) {
+               ath10k_warn("could not read hi_failure_state\n");
+               return;
+       }
+
+       ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area);
+
+       ret = ath10k_pci_diag_read_mem(ar, reg_dump_area,
+                                      &reg_dump_values[0],
+                                      REG_DUMP_COUNT_QCA988X * sizeof(u32));
+       if (ret != 0) {
+               ath10k_err("could not dump FW Dump Area\n");
+               return;
+       }
+
+       BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4);
+
+       ath10k_err("target Register Dump\n");
+       for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4)
+               ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
+                          i,
+                          reg_dump_values[i],
+                          reg_dump_values[i + 1],
+                          reg_dump_values[i + 2],
+                          reg_dump_values[i + 3]);
+}
+
+static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+                                              int force)
+{
+       if (!force) {
+               int resources;
+               /*
+                * Decide whether to actually poll for completions, or just
+                * wait for a later chance.
+                * If there seem to be plenty of resources left, then just wait
+                * since checking involves reading a CE register, which is a
+                * relatively expensive operation.
+                */
+               resources = ath10k_pci_hif_get_free_queue_number(ar, pipe);
+
+               /*
+                * If at least 50% of the total resources are still available,
+                * don't bother checking again yet.
+                */
+               if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1))
+                       return;
+       }
+       ath10k_ce_per_engine_service(ar, pipe);
+}
+
+static void ath10k_pci_hif_post_init(struct ath10k *ar,
+                                    struct ath10k_hif_cb *callbacks)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       memcpy(&ar_pci->msg_callbacks_current, callbacks,
+              sizeof(ar_pci->msg_callbacks_current));
+}
+
+static int ath10k_pci_start_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_diag = ar_pci->ce_diag;
+       const struct ce_attr *attr;
+       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_compl *compl;
+       int i, pipe_num, completions, disable_interrupts;
+
+       spin_lock_init(&ar_pci->compl_lock);
+       INIT_LIST_HEAD(&ar_pci->compl_process);
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_init(&pipe_info->pipe_lock);
+               INIT_LIST_HEAD(&pipe_info->compl_free);
+
+               /* Handle Diagnostic CE specially */
+               if (pipe_info->ce_hdl == ce_diag)
+                       continue;
+
+               attr = &host_ce_config_wlan[pipe_num];
+               completions = 0;
+
+               if (attr->src_nentries) {
+                       disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
+                       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_send_done,
+                                                  disable_interrupts);
+                       completions += attr->src_nentries;
+                       pipe_info->num_sends_allowed = attr->src_nentries - 1;
+               }
+
+               if (attr->dest_nentries) {
+                       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_recv_data);
+                       completions += attr->dest_nentries;
+               }
+
+               if (completions == 0)
+                       continue;
+
+               for (i = 0; i < completions; i++) {
+                       compl = kmalloc(sizeof(struct ath10k_pci_compl),
+                                       GFP_KERNEL);
+                       if (!compl) {
+                               ath10k_warn("No memory for completion state\n");
+                               ath10k_pci_stop_ce(ar);
+                               return -ENOMEM;
+                       }
+
+                       compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+                       list_add_tail(&compl->list, &pipe_info->compl_free);
+               }
+       }
+
+       return 0;
+}
+
+static void ath10k_pci_stop_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       int i;
+
+       ath10k_ce_disable_interrupts(ar);
+
+       /* Cancel the pending tasklet */
+       tasklet_kill(&ar_pci->intr_tq);
+
+       for (i = 0; i < CE_COUNT; i++)
+               tasklet_kill(&ar_pci->pipe_info[i].intr);
+
+       /* Mark pending completions as aborted, so that upper layers free up
+        * their associated resources */
+       spin_lock_bh(&ar_pci->compl_lock);
+       list_for_each_entry(compl, &ar_pci->compl_process, list) {
+               skb = (struct sk_buff *)compl->transfer_context;
+               ATH10K_SKB_CB(skb)->is_aborted = true;
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+static void ath10k_pci_cleanup_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl, *tmp;
+       struct hif_ce_pipe_info *pipe_info;
+       struct sk_buff *netbuf;
+       int pipe_num;
+
+       /* Free pending completions. */
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (!list_empty(&ar_pci->compl_process))
+               ath10k_warn("pending completions still present! possible memory leaks.\n");
+
+       list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
+               list_del(&compl->list);
+               netbuf = (struct sk_buff *)compl->transfer_context;
+               dev_kfree_skb_any(netbuf);
+               kfree(compl);
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       /* Free unused completions for each pipe. */
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_bh(&pipe_info->pipe_lock);
+               list_for_each_entry_safe(compl, tmp,
+                                        &pipe_info->compl_free, list) {
+                       list_del(&compl->list);
+                       kfree(compl);
+               }
+               spin_unlock_bh(&pipe_info->pipe_lock);
+       }
+}
+
+static void ath10k_pci_process_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ar->hif.priv;
+       struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       unsigned int nbytes;
+       int ret, send_done = 0;
+
+       /* Upper layers aren't ready to handle tx/rx completions in parallel so
+        * we must serialize all completion processing. */
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (ar_pci->compl_processing) {
+               spin_unlock_bh(&ar_pci->compl_lock);
+               return;
+       }
+       ar_pci->compl_processing = true;
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       for (;;) {
+               spin_lock_bh(&ar_pci->compl_lock);
+               if (list_empty(&ar_pci->compl_process)) {
+                       spin_unlock_bh(&ar_pci->compl_lock);
+                       break;
+               }
+               compl = list_first_entry(&ar_pci->compl_process,
+                                        struct ath10k_pci_compl, list);
+               list_del(&compl->list);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
+                       cb->tx_completion(ar,
+                                         compl->transfer_context,
+                                         compl->transfer_id);
+                       send_done = 1;
+               } else {
+                       ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
+                       if (ret) {
+                               ath10k_warn("Unable to post recv buffer for pipe: %d\n",
+                                           compl->pipe_info->pipe_num);
+                               break;
+                       }
+
+                       skb = (struct sk_buff *)compl->transfer_context;
+                       nbytes = compl->nbytes;
+
+                       ath10k_dbg(ATH10K_DBG_PCI,
+                                  "ath10k_pci_ce_recv_data netbuf=%p  nbytes=%d\n",
+                                  skb, nbytes);
+                       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                                       "ath10k rx: ", skb->data, nbytes);
+
+                       if (skb->len + skb_tailroom(skb) >= nbytes) {
+                               skb_trim(skb, 0);
+                               skb_put(skb, nbytes);
+                               cb->rx_completion(ar, skb,
+                                                 compl->pipe_info->pipe_num);
+                       } else {
+                               ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+                                           nbytes,
+                                           skb->len + skb_tailroom(skb));
+                       }
+               }
+
+               compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+
+               /*
+                * Add completion back to the pipe's free list.
+                */
+               spin_lock_bh(&compl->pipe_info->pipe_lock);
+               list_add_tail(&compl->list, &compl->pipe_info->compl_free);
+               compl->pipe_info->num_sends_allowed += send_done;
+               spin_unlock_bh(&compl->pipe_info->pipe_lock);
+       }
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       ar_pci->compl_processing = false;
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+/* TODO - temporary mapping while we have too few CE's */
+static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
+                                             u16 service_id, u8 *ul_pipe,
+                                             u8 *dl_pipe, int *ul_is_polled,
+                                             int *dl_is_polled)
+{
+       int ret = 0;
+
+       /* polling for received messages not supported */
+       *dl_is_polled = 0;
+
+       switch (service_id) {
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               /*
+                * Host->target HTT gets its own pipe, so it can be polled
+                * while other pipes are interrupt driven.
+                */
+               *ul_pipe = 4;
+               /*
+                * Use the same target->host pipe for HTC ctrl, HTC raw
+                * streams, and HTT.
+                */
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               /*
+                * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+                * HTC_CTRL_RSVD_SVC could share the same pipe as the
+                * WMI services.  So, if another CE is needed, change
+                * this to *ul_pipe = 3, which frees up CE 0.
+                */
+               /* *ul_pipe = 3; */
+               *ul_pipe = 0;
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               *ul_pipe = 3;
+               *dl_pipe = 2;
+               break;
+
+               /* pipe 5 unused   */
+               /* pipe 6 reserved */
+               /* pipe 7 reserved */
+
+       default:
+               ret = -1;
+               break;
+       }
+       *ul_is_polled =
+               (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
+
+       return ret;
+}
+
+static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+                                               u8 *ul_pipe, u8 *dl_pipe)
+{
+       int ul_is_polled, dl_is_polled;
+
+       (void)ath10k_pci_hif_map_service_to_pipe(ar,
+                                                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                                                ul_pipe,
+                                                dl_pipe,
+                                                &ul_is_polled,
+                                                &dl_is_polled);
+}
+
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+                                  int num)
+{
+       struct ath10k *ar = pipe_info->hif_ce_state;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = pipe_info->ce_hdl;
+       struct sk_buff *skb;
+       dma_addr_t ce_data;
+       int i, ret = 0;
+
+       if (pipe_info->buf_sz == 0)
+               return 0;
+
+       for (i = 0; i < num; i++) {
+               skb = dev_alloc_skb(pipe_info->buf_sz);
+               if (!skb) {
+                       ath10k_warn("could not allocate skbuff for pipe %d\n",
+                                   num);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+               ce_data = dma_map_single(ar->dev, skb->data,
+                                        skb->len + skb_tailroom(skb),
+                                        DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
+                       ath10k_warn("could not dma map skbuff\n");
+                       dev_kfree_skb_any(skb);
+                       ret = -EIO;
+                       goto err;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = ce_data;
+
+               pci_dma_sync_single_for_device(ar_pci->pdev, ce_data,
+                                              pipe_info->buf_sz,
+                                              PCI_DMA_FROMDEVICE);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
+                                                ce_data);
+               if (ret) {
+                       ath10k_warn("could not enqueue to pipe %d (%d)\n",
+                                   num, ret);
+                       goto err;
+               }
+       }
+
+       return ret;
+
+err:
+       ath10k_pci_rx_pipe_cleanup(pipe_info);
+       return ret;
+}
+
+static int ath10k_pci_post_rx(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num, ret = 0;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               attr = &host_ce_config_wlan[pipe_num];
+
+               if (attr->dest_nentries == 0)
+                       continue;
+
+               ret = ath10k_pci_post_rx_pipe(pipe_info,
+                                             attr->dest_nentries - 1);
+               if (ret) {
+                       ath10k_warn("Unable to replenish recv buffers for pipe: %d\n",
+                                   pipe_num);
+
+                       for (; pipe_num >= 0; pipe_num--) {
+                               pipe_info = &ar_pci->pipe_info[pipe_num];
+                               ath10k_pci_rx_pipe_cleanup(pipe_info);
+                       }
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath10k_pci_hif_start(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = ath10k_pci_start_ce(ar);
+       if (ret) {
+               ath10k_warn("could not start CE (%d)\n", ret);
+               return ret;
+       }
+
+       /* Post buffers once to start things off. */
+       ret = ath10k_pci_post_rx(ar);
+       if (ret) {
+               ath10k_warn("could not post rx pipes (%d)\n", ret);
+               return ret;
+       }
+
+       ar_pci->started = 1;
+       return 0;
+}
+
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       u32 buf_sz;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data) == 0) {
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
+                                netbuf->len + skb_tailroom(netbuf),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(netbuf);
+       }
+}
+
+static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int id;
+       u32 buf_sz;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data, &nbytes, &id) == 0) {
+               if (netbuf != CE_SENDLIST_ITEM_CTXT)
+                       /*
+                        * Indicate the completion to higer layer to free
+                        * the buffer
+                        */
+                       ATH10K_SKB_CB(netbuf)->is_aborted = true;
+                       ar_pci->msg_callbacks_current.tx_completion(ar,
+                                                                   netbuf,
+                                                                   id);
+       }
+}
+
+/*
+ * Cleanup residual buffers for device shutdown:
+ *    buffers that were enqueued for receive
+ *    buffers that were to be sent
+ * Note: Buffers that had completed but which were
+ * not yet processed are on a completion queue. They
+ * are handled when the completion thread shuts down.
+ */
+static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               struct hif_ce_pipe_info *pipe_info;
+
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               ath10k_pci_rx_pipe_cleanup(pipe_info);
+               ath10k_pci_tx_pipe_cleanup(pipe_info);
+       }
+}
+
+static void ath10k_pci_ce_deinit(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               if (pipe_info->ce_hdl) {
+                       ath10k_ce_deinit(pipe_info->ce_hdl);
+                       pipe_info->ce_hdl = NULL;
+                       pipe_info->buf_sz = 0;
+               }
+       }
+}
+
+static void ath10k_pci_hif_stop(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ath10k_pci_stop_ce(ar);
+
+       /* At this point, asynchronous threads are stopped, the target should
+        * not DMA nor interrupt. We process the leftovers and then free
+        * everything else up. */
+
+       ath10k_pci_process_ce(ar);
+       ath10k_pci_cleanup_ce(ar);
+       ath10k_pci_buffer_cleanup(ar);
+       ath10k_pci_ce_deinit(ar);
+}
+
+static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+                                          void *req, u32 req_len,
+                                          void *resp, u32 *resp_len)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
+       struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+       dma_addr_t req_paddr = 0;
+       dma_addr_t resp_paddr = 0;
+       struct bmi_xfer xfer = {};
+       void *treq, *tresp = NULL;
+       int ret = 0;
+
+       if (resp && !resp_len)
+               return -EINVAL;
+
+       if (resp && resp_len && *resp_len == 0)
+               return -EINVAL;
+
+       treq = kmemdup(req, req_len, GFP_KERNEL);
+       if (!treq)
+               return -ENOMEM;
+
+       req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE);
+       ret = dma_mapping_error(ar->dev, req_paddr);
+       if (ret)
+               goto err_dma;
+
+       if (resp && resp_len) {
+               tresp = kzalloc(*resp_len, GFP_KERNEL);
+               if (!tresp) {
+                       ret = -ENOMEM;
+                       goto err_req;
+               }
+
+               resp_paddr = dma_map_single(ar->dev, tresp, *resp_len,
+                                           DMA_FROM_DEVICE);
+               ret = dma_mapping_error(ar->dev, resp_paddr);
+               if (ret)
+                       goto err_req;
+
+               xfer.wait_for_resp = true;
+               xfer.resp_len = 0;
+
+               ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
+       }
+
+       init_completion(&xfer.done);
+
+       ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
+       if (ret)
+               goto err_resp;
+
+       ret = wait_for_completion_timeout(&xfer.done,
+                                         BMI_COMMUNICATION_TIMEOUT_HZ);
+       if (ret <= 0) {
+               u32 unused_buffer;
+               unsigned int unused_nbytes;
+               unsigned int unused_id;
+
+               ret = -ETIMEDOUT;
+               ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer,
+                                          &unused_nbytes, &unused_id);
+       } else {
+               /* non-zero means we did not time out */
+               ret = 0;
+       }
+
+err_resp:
+       if (resp) {
+               u32 unused_buffer;
+
+               ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer);
+               dma_unmap_single(ar->dev, resp_paddr,
+                                *resp_len, DMA_FROM_DEVICE);
+       }
+err_req:
+       dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE);
+
+       if (ret == 0 && resp_len) {
+               *resp_len = min(*resp_len, xfer.resp_len);
+               memcpy(resp, tresp, xfer.resp_len);
+       }
+err_dma:
+       kfree(treq);
+       kfree(tresp);
+
+       return ret;
+}
+
+static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (xfer->wait_for_resp)
+               return;
+
+       complete(&xfer->done);
+}
+
+static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id,
+                                    unsigned int flags)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (!xfer->wait_for_resp) {
+               ath10k_warn("unexpected: BMI data received; ignoring\n");
+               return;
+       }
+
+       xfer->resp_len = nbytes;
+       complete(&xfer->done);
+}
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
+ * It is passed to the Target at startup for use by firmware.
+ */
+static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,             /* could be moved to 3 (share with WMI) */
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                4,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+
+       /* (Additions here) */
+
+       {                               /* Must be last */
+                0,
+                0,
+                0,
+       },
+};
+
+/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
+{
+       int ret;
+       u32 core_ctrl;
+
+       ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS |
+                                             CORE_CTRL_ADDRESS,
+                                         &core_ctrl);
+       if (ret) {
+               ath10k_warn("Unable to read core ctrl\n");
+               return ret;
+       }
+
+       /* A_INUM_FIRMWARE interrupt to Target CPU */
+       core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
+
+       ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
+                                              CORE_CTRL_ADDRESS,
+                                          core_ctrl);
+       if (ret)
+               ath10k_warn("Unable to set interrupt mask\n");
+
+       return ret;
+}
+
+static int ath10k_pci_init_config(struct ath10k *ar)
+{
+       u32 interconnect_targ_addr;
+       u32 pcie_state_targ_addr = 0;
+       u32 pipe_cfg_targ_addr = 0;
+       u32 svc_to_pipe_map = 0;
+       u32 pcie_config_flags = 0;
+       u32 ealloc_value;
+       u32 ealloc_targ_addr;
+       u32 flag2_value;
+       u32 flag2_targ_addr;
+       int ret = 0;
+
+       /* Download to Target the CE Config and the service-to-CE map */
+       interconnect_targ_addr =
+               host_interest_item_address(HI_ITEM(hi_interconnect_state));
+
+       /* Supply Target-side CE configuration */
+       ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr,
+                                         &pcie_state_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie state addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pcie_state_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pcie state addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  pipe_cfg_addr),
+                                         &pipe_cfg_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pipe cfg addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pipe_cfg_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pipe cfg addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
+                                target_ce_config_wlan,
+                                sizeof(target_ce_config_wlan));
+
+       if (ret != 0) {
+               ath10k_err("Failed to write pipe cfg: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  svc_to_pipe_map),
+                                         &svc_to_pipe_map);
+       if (ret != 0) {
+               ath10k_err("Failed to get svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       if (svc_to_pipe_map == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid svc_to_pipe map\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
+                                target_service_to_ce_map_wlan,
+                                sizeof(target_service_to_ce_map_wlan));
+       if (ret != 0) {
+               ath10k_err("Failed to write svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  config_flags),
+                                         &pcie_config_flags);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+
+       ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr +
+                                offsetof(struct pcie_state, config_flags),
+                                &pcie_config_flags,
+                                sizeof(pcie_config_flags));
+       if (ret != 0) {
+               ath10k_err("Failed to write pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       /* configure early allocation */
+       ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
+
+       ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Faile to get early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* first bank is switched to IRAM */
+       ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+                        HI_EARLY_ALLOC_MAGIC_MASK);
+       ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+                        HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+       ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* Tell Target to proceed with initialization */
+       flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
+
+       ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to get option val: %d\n", ret);
+               return ret;
+       }
+
+       flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+
+       ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set option val: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+
+static int ath10k_pci_ce_init(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               pipe_info->pipe_num = pipe_num;
+               pipe_info->hif_ce_state = ar;
+               attr = &host_ce_config_wlan[pipe_num];
+
+               pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
+               if (pipe_info->ce_hdl == NULL) {
+                       ath10k_err("Unable to initialize CE for pipe: %d\n",
+                                  pipe_num);
+
+                       /* It is safe to call it here. It checks if ce_hdl is
+                        * valid for each pipe */
+                       ath10k_pci_ce_deinit(ar);
+                       return -1;
+               }
+
+               if (pipe_num == ar_pci->ce_count - 1) {
+                       /*
+                        * Reserve the ultimate CE for
+                        * diagnostic Window support
+                        */
+                       ar_pci->ce_diag =
+                       ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl;
+                       continue;
+               }
+
+               pipe_info->buf_sz = (size_t) (attr->src_sz_max);
+       }
+
+       /*
+        * Initially, establish CE completion handlers for use with BMI.
+        * These are overwritten with generic handlers after we exit BMI phase.
+        */
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_send_done, 0);
+
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_recv_data);
+
+       return 0;
+}
+
+static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       u32 fw_indicator_address, fw_indicator;
+
+       ath10k_pci_wake(ar);
+
+       fw_indicator_address = ar_pci->fw_indicator_address;
+       fw_indicator = ath10k_pci_read32(ar, fw_indicator_address);
+
+       if (fw_indicator & FW_IND_EVENT_PENDING) {
+               /* ACK: clear Target-side pending event */
+               ath10k_pci_write32(ar, fw_indicator_address,
+                                  fw_indicator & ~FW_IND_EVENT_PENDING);
+
+               if (ar_pci->started) {
+                       ath10k_pci_hif_dump_area(ar);
+               } else {
+                       /*
+                        * Probable Target failure before we're prepared
+                        * to handle it.  Generally unexpected.
+                        */
+                       ath10k_warn("early firmware event indicated\n");
+               }
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+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,
+       .start                  = ath10k_pci_hif_start,
+       .stop                   = ath10k_pci_hif_stop,
+       .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,
+       .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
+};
+
+static void ath10k_pci_ce_tasklet(unsigned long ptr)
+{
+       struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+       struct ath10k_pci *ar_pci = pipe->ar_pci;
+
+       ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
+}
+
+static void ath10k_msi_err_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+
+       ath10k_pci_fw_interrupt_handler(ar);
+}
+
+/*
+ * Handler for a per-engine interrupt on a PARTICULAR CE.
+ * This is used in cases where each CE has a private MSI interrupt.
+ */
+static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL;
+
+       if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) {
+               ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id);
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * NOTE: We are able to derive ce_id from irq because we
+        * use a one-to-one mapping for CE's 0..5.
+        * CE's 6 & 7 do not use interrupts at all.
+        *
+        * This mapping must be kept in sync with the mapping
+        * used by firmware.
+        */
+       tasklet_schedule(&ar_pci->pipe_info[ce_id].intr);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       tasklet_schedule(&ar_pci->msi_fw_err);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Top-level interrupt handler for all PCI interrupts from a Target.
+ * When a block of MSI interrupts is allocated, this top-level handler
+ * is not used; instead, we directly call the correct sub-handler.
+ */
+static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /*
+                * IMPORTANT: INTR_CLR regiser has to be set after
+                * INTR_ENABLE is set to 0, otherwise interrupt can not be
+                * really cleared.
+                */
+               iowrite32(0, ar_pci->mem +
+                         (SOC_CORE_BASE_ADDRESS |
+                          PCIE_INTR_ENABLE_ADDRESS));
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_CLR_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer.
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+
+       tasklet_schedule(&ar_pci->intr_tq);
+
+       return IRQ_HANDLED;
+}
+
+static void ath10k_pci_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
+       ath10k_ce_per_engine_service_any(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /* Enable Legacy PCI line interrupts */
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_ENABLE_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+}
+
+static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+       int i;
+
+       ret = pci_enable_msi_block(ar_pci->pdev, num);
+       if (ret)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
+                         ath10k_pci_msi_fw_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret)
+               return ret;
+
+       for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
+               ret = request_irq(ar_pci->pdev->irq + i,
+                                 ath10k_pci_per_engine_handler,
+                                 IRQF_SHARED, "ath10k_pci", ar);
+               if (ret) {
+                       ath10k_warn("request_irq(%d) failed %d\n",
+                                   ar_pci->pdev->irq + i, ret);
+
+                       for (; i >= MSI_ASSIGN_CE_INITIAL; i--)
+                               free_irq(ar_pci->pdev->irq, ar);
+
+                       pci_disable_msi(ar_pci->pdev);
+                       return ret;
+               }
+       }
+
+       ath10k_info("MSI-X interrupt handling (%d intrs)\n", num);
+       return 0;
+}
+
+static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = pci_enable_msi(ar_pci->pdev);
+       if (ret < 0)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0) {
+               pci_disable_msi(ar_pci->pdev);
+               return ret;
+       }
+
+       ath10k_info("MSI interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Make sure to wake the Target before enabling Legacy
+        * Interrupt.
+        */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       /*
+        * A potential race occurs here: The CORE_BASE write
+        * depends on target correctly decoding AXI address but
+        * host won't know when target writes BAR to CORE_CTRL.
+        * This write might get lost if target has NOT written BAR.
+        * For now, fix the race by repeating the write in below
+        * synchronization checking.
+        */
+       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                 PCIE_INTR_CE_MASK_ALL,
+                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_info("legacy interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int num = MSI_NUM_REQUEST;
+       int ret;
+       int i;
+
+       tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
+       tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
+                    (unsigned long) ar);
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ar_pci->pipe_info[i].ar_pci = ar_pci;
+               tasklet_init(&ar_pci->pipe_info[i].intr,
+                            ath10k_pci_ce_tasklet,
+                            (unsigned long)&ar_pci->pipe_info[i]);
+       }
+
+       if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
+               num = 1;
+
+       if (num > 1) {
+               ret = ath10k_pci_start_intr_msix(ar, num);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret);
+               num = 1;
+       }
+
+       if (num == 1) {
+               ret = ath10k_pci_start_intr_msi(ar);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n",
+                           ret);
+               num = 0;
+       }
+
+       ret = ath10k_pci_start_intr_legacy(ar);
+
+exit:
+       ar_pci->num_msi_intrs = num;
+       ar_pci->ce_count = CE_COUNT;
+       return ret;
+}
+
+static void ath10k_pci_stop_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int i;
+
+       /* There's at least one interrupt irregardless whether its legacy INTR
+        * or MSI or MSI-X */
+       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+               free_irq(ar_pci->pdev->irq + i, ar);
+
+       if (ar_pci->num_msi_intrs > 0)
+               pci_disable_msi(ar_pci->pdev);
+}
+
+static int ath10k_pci_reset_target(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int wait_limit = 300; /* 3 sec */
+
+       /* Wait for Target to finish initialization before we proceed. */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       while (wait_limit-- &&
+              !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
+                FW_IND_INITIALIZED)) {
+               if (ar_pci->num_msi_intrs == 0)
+                       /* Fix potential race by repeating CORE_BASE writes */
+                       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                                 PCIE_INTR_CE_MASK_ALL,
+                                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                                PCIE_INTR_ENABLE_ADDRESS));
+               mdelay(10);
+       }
+
+       if (wait_limit < 0) {
+               ath10k_err("Target stalled\n");
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+               return -EIO;
+       }
+
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       return 0;
+}
+
+static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+{
+       struct ath10k *ar = ar_pci->ar;
+       void __iomem *mem = ar_pci->mem;
+       int i;
+       u32 val;
+
+       if (!SOC_GLOBAL_RESET_ADDRESS)
+               return;
+
+       if (!mem)
+               return;
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
+                              PCIE_SOC_WAKE_V_MASK);
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_target_is_awake(ar))
+                       break;
+               msleep(1);
+       }
+
+       /* Put Target, including PCIe, into RESET. */
+       val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+       val |= 1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                         RTC_STATE_COLD_RESET_MASK)
+                       break;
+               msleep(1);
+       }
+
+       /* Pull Target, including PCIe, out of RESET. */
+       val &= ~1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                           RTC_STATE_COLD_RESET_MASK))
+                       break;
+               msleep(1);
+       }
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+}
+
+static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
+{
+       int i;
+
+       for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) {
+               if (!test_bit(i, ar_pci->features))
+                       continue;
+
+               switch (i) {
+               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:
+                       ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+                       break;
+               }
+       }
+}
+
+static int ath10k_pci_probe(struct pci_dev *pdev,
+                           const struct pci_device_id *pci_dev)
+{
+       void __iomem *mem;
+       int ret = 0;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       u32 lcr_val;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
+       if (ar_pci == NULL)
+               return -ENOMEM;
+
+       ar_pci->pdev = pdev;
+       ar_pci->dev = &pdev->dev;
+
+       switch (pci_dev->device) {
+       case QCA988X_1_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+               break;
+       case QCA988X_2_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
+               break;
+       default:
+               ret = -ENODEV;
+               ath10k_err("Unkown device ID: %d\n", pci_dev->device);
+               goto err_ar_pci;
+       }
+
+       ath10k_pci_dump_features(ar_pci);
+
+       ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
+                               &ath10k_pci_hif_ops);
+       if (!ar) {
+               ath10k_err("ath10k_core_create failed!\n");
+               ret = -EINVAL;
+               goto err_ar_pci;
+       }
+
+       /* Enable QCA988X_1.0 HW workarounds */
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+               spin_lock_init(&ar_pci->hw_v1_workaround_lock);
+
+       ar_pci->ar = ar;
+       ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
+       atomic_set(&ar_pci->keep_awake_count, 0);
+
+       pci_set_drvdata(pdev, ar);
+
+       /*
+        * Without any knowledge of the Host, the Target may have been reset or
+        * power cycled and its Config Space may no longer reflect the PCI
+        * address space that was assigned earlier by the PCI infrastructure.
+        * Refresh it now.
+        */
+       ret = pci_assign_resource(pdev, BAR_NUM);
+       if (ret) {
+               ath10k_err("cannot assign PCI space: %d\n", ret);
+               goto err_ar;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               ath10k_err("cannot enable PCI device: %d\n", ret);
+               goto err_ar;
+       }
+
+       /* Request MMIO resources */
+       ret = pci_request_region(pdev, BAR_NUM, "ath");
+       if (ret) {
+               ath10k_err("PCI MMIO reservation error: %d\n", ret);
+               goto err_device;
+       }
+
+       /*
+        * Target structures have a limit of 32 bit DMA pointers.
+        * DMA pointers can be wider than 32 bits by default on some systems.
+        */
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("32-bit DMA not available: %d\n", ret);
+               goto err_region;
+       }
+
+       ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("cannot enable 32-bit consistent DMA\n");
+               goto err_region;
+       }
+
+       /* Set bus master bit in PCI_COMMAND to enable DMA */
+       pci_set_master(pdev);
+
+       /*
+        * Temporary FIX: disable ASPM
+        * Will be removed after the OTP is programmed
+        */
+       pci_read_config_dword(pdev, 0x80, &lcr_val);
+       pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
+
+       /* Arrange for access to Target SoC registers. */
+       mem = pci_iomap(pdev, BAR_NUM, 0);
+       if (!mem) {
+               ath10k_err("PCI iomap error\n");
+               ret = -EIO;
+               goto err_master;
+       }
+
+       ar_pci->mem = mem;
+
+       spin_lock_init(&ar_pci->ce_lock);
+
+       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;
+       }
+
+       return 0;
+
+err_ce:
+       ath10k_pci_ce_deinit(ar);
+err_intr:
+       ath10k_pci_stop_intr(ar);
+err_iomap:
+       pci_iounmap(pdev, mem);
+err_master:
+       pci_clear_master(pdev);
+err_region:
+       pci_release_region(pdev, BAR_NUM);
+err_device:
+       pci_disable_device(pdev);
+err_ar:
+       pci_set_drvdata(pdev, NULL);
+       ath10k_core_destroy(ar);
+err_ar_pci:
+       /* call HIF PCI free here */
+       kfree(ar_pci);
+
+       return ret;
+}
+
+static void ath10k_pci_remove(struct pci_dev *pdev)
+{
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return;
+
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci)
+               return;
+
+       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);
+       pci_release_region(pdev, BAR_NUM);
+       pci_clear_master(pdev);
+       pci_disable_device(pdev);
+
+       ath10k_core_destroy(ar);
+       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 = {
+       .name = "ath10k_pci",
+       .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)
+{
+       int ret;
+
+       ret = pci_register_driver(&ath10k_pci_driver);
+       if (ret)
+               ath10k_err("pci_register_driver failed [%d]\n", ret);
+
+       return ret;
+}
+module_init(ath10k_pci_init);
+
+static void __exit ath10k_pci_exit(void)
+{
+       pci_unregister_driver(&ath10k_pci_driver);
+}
+
+module_exit(ath10k_pci_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
new file mode 100644 (file)
index 0000000..d2a055a
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PCI_H_
+#define _PCI_H_
+
+#include <linux/interrupt.h>
+
+#include "hw.h"
+#include "ce.h"
+
+/* FW dump area */
+#define REG_DUMP_COUNT_QCA988X 60
+
+/*
+ * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+/*
+ * maximum number of bytes that can be
+ * handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+struct bmi_xfer {
+       struct completion done;
+       bool wait_for_resp;
+       u32 resp_len;
+};
+
+struct ath10k_pci_compl {
+       struct list_head list;
+       int send_or_recv;
+       struct ce_state *ce_state;
+       struct hif_ce_pipe_info *pipe_info;
+       void *transfer_context;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+       unsigned int flags;
+};
+
+/* compl_state.send_or_recv */
+#define HIF_CE_COMPLETE_FREE 0
+#define HIF_CE_COMPLETE_SEND 1
+#define HIF_CE_COMPLETE_RECV 2
+
+/*
+ * PCI-specific Target state
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ *
+ * Much of this may be of interest to the Host so
+ * HOST_INTEREST->hi_interconnect_state points here
+ * (and all members are 32-bit quantities in order to
+ * facilitate Host access). In particular, Host software is
+ * required to initialize pipe_cfg_addr and svc_to_pipe_map.
+ */
+struct pcie_state {
+       /* Pipe configuration Target address */
+       /* NB: ce_pipe_config[CE_COUNT] */
+       u32 pipe_cfg_addr;
+
+       /* Service to pipe map Target address */
+       /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */
+       u32 svc_to_pipe_map;
+
+       /* number of MSI interrupts requested */
+       u32 msi_requested;
+
+       /* number of MSI interrupts granted */
+       u32 msi_granted;
+
+       /* Message Signalled Interrupt address */
+       u32 msi_addr;
+
+       /* Base data */
+       u32 msi_data;
+
+       /*
+        * Data for firmware interrupt;
+        * MSI data for other interrupts are
+        * in various SoC registers
+        */
+       u32 msi_fw_intr_data;
+
+       /* PCIE_PWR_METHOD_* */
+       u32 power_mgmt_method;
+
+       /* PCIE_CONFIG_FLAG_* */
+       u32 config_flags;
+};
+
+/* PCIE_CONFIG_FLAG definitions */
+#define PCIE_CONFIG_FLAG_ENABLE_L1  0x0000001
+
+/* Host software's Copy Engine configuration. */
+#define CE_ATTR_FLAGS 0
+
+/*
+ * Configuration information for a Copy Engine pipe.
+ * Passed from Host to Target during startup (one per CE).
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ */
+struct ce_pipe_config {
+       u32 pipenum;
+       u32 pipedir;
+       u32 nentries;
+       u32 nbytes_max;
+       u32 flags;
+       u32 reserved;
+};
+
+/*
+ * Directions for interconnect pipe configuration.
+ * These definitions may be used during configuration and are shared
+ * between Host and Target.
+ *
+ * Pipe Directions are relative to the Host, so PIPEDIR_IN means
+ * "coming IN over air through Target to Host" as with a WiFi Rx operation.
+ * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air"
+ * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man"
+ * Target since things that are "PIPEDIR_OUT" are coming IN to the Target
+ * over the interconnect.
+ */
+#define PIPEDIR_NONE    0
+#define PIPEDIR_IN      1  /* Target-->Host, WiFi Rx direction */
+#define PIPEDIR_OUT     2  /* Host->Target, WiFi Tx direction */
+#define PIPEDIR_INOUT   3  /* bidirectional */
+
+/* Establish a mapping between a service/direction and a pipe. */
+struct service_to_pipe {
+       u32 service_id;
+       u32 pipedir;
+       u32 pipenum;
+};
+
+enum ath10k_pci_features {
+       ATH10K_PCI_FEATURE_MSI_X                = 0,
+       ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND    = 1,
+
+       /* keep last */
+       ATH10K_PCI_FEATURE_COUNT
+};
+
+/* Per-pipe state. */
+struct hif_ce_pipe_info {
+       /* Handle of underlying Copy Engine */
+       struct ce_state *ce_hdl;
+
+       /* Our pipe number; facilitiates use of pipe_info ptrs. */
+       u8 pipe_num;
+
+       /* Convenience back pointer to hif_ce_state. */
+       struct ath10k *hif_ce_state;
+
+       size_t buf_sz;
+
+       /* protects compl_free and num_send_allowed */
+       spinlock_t pipe_lock;
+
+       /* List of free CE completion slots */
+       struct list_head compl_free;
+
+       /* Limit the number of outstanding send requests. */
+       int num_sends_allowed;
+
+       struct ath10k_pci *ar_pci;
+       struct tasklet_struct intr;
+};
+
+struct ath10k_pci {
+       struct pci_dev *pdev;
+       struct device *dev;
+       struct ath10k *ar;
+       void __iomem *mem;
+       int cacheline_sz;
+
+       DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
+
+       /*
+        * Number of MSI interrupts granted, 0 --> using legacy PCI line
+        * interrupts.
+        */
+       int num_msi_intrs;
+
+       struct tasklet_struct intr_tq;
+       struct tasklet_struct msi_fw_err;
+
+       /* Number of Copy Engines supported */
+       unsigned int ce_count;
+
+       int started;
+
+       atomic_t keep_awake_count;
+       bool verified_awake;
+
+       /* List of CE completions to be processed */
+       struct list_head compl_process;
+
+       /* protects compl_processing and compl_process */
+       spinlock_t compl_lock;
+
+       bool compl_processing;
+
+       struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+
+       struct ath10k_hif_cb msg_callbacks_current;
+
+       /* Target address used to signal a pending firmware event */
+       u32 fw_indicator_address;
+
+       /* Copy Engine used for Diagnostic Accesses */
+       struct ce_state *ce_diag;
+
+       /* FIXME: document what this really protects */
+       spinlock_t ce_lock;
+
+       /* Map CE id to ce_state */
+       struct ce_state *ce_id_to_state[CE_COUNT_MAX];
+
+       /* makes sure that dummy reads are atomic */
+       spinlock_t hw_v1_workaround_lock;
+};
+
+static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
+{
+       return ar->hif.priv;
+}
+
+static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+{
+       return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+{
+       iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
+#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */
+
+#define BAR_NUM 0
+
+#define CDC_WAR_MAGIC_STR   0xceef0000
+#define CDC_WAR_DATA_CE     4
+
+/*
+ * TODO: Should be a function call specific to each Target-type.
+ * This convoluted macro converts from Target CPU Virtual Address Space to CE
+ * Address Space. As part of this process, we conservatively fetch the current
+ * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space
+ * for this device; but that's not guaranteed.
+ */
+#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr)                 \
+       (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS|                  \
+         CORE_CTRL_ADDRESS)) & 0x7ff) << 21) |                         \
+        0x100000 | ((addr) & 0xfffff))
+
+/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+#define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+/*
+ * This API allows the Host to access Target registers directly
+ * and relatively efficiently over PCIe.
+ * This allows the Host to avoid extra overhead associated with
+ * sending a message to firmware and waiting for a response message
+ * from firmware, as is done on other interconnects.
+ *
+ * Yet there is some complexity with direct accesses because the
+ * Target's power state is not known a priori. The Host must issue
+ * special PCIe reads/writes in order to explicitly wake the Target
+ * and to verify that it is awake and will remain awake.
+ *
+ * Usage:
+ *
+ *   Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space.
+ *   These calls must be bracketed by ath10k_pci_wake and
+ *   ath10k_pci_sleep.  A single BEGIN/END pair is adequate for
+ *   multiple READ/WRITE operations.
+ *
+ *   Use ath10k_pci_wake to put the Target in a state in
+ *   which it is legal for the Host to directly access it. This
+ *   may involve waking the Target from a low power state, which
+ *   may take up to 2Ms!
+ *
+ *   Use ath10k_pci_sleep to tell the Target that as far as
+ *   this code path is concerned, it no longer needs to remain
+ *   directly accessible.  BEGIN/END is under a reference counter;
+ *   multiple code paths may issue BEGIN/END on a single targid.
+ */
+static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
+                                     u32 value)
+{
+       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)) {
+               unsigned long irq_flags;
+
+               spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
+
+               ioread32(addr+offset+4); /* 3rd read prior to write */
+               ioread32(addr+offset+4); /* 2nd read prior to write */
+               ioread32(addr+offset+4); /* 1st read prior to write */
+               iowrite32(value, addr+offset);
+
+               spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
+                                      irq_flags);
+       } else {
+               iowrite32(value, addr+offset);
+       }
+}
+
+static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       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)
+               ath10k_do_pci_wake(ar);
+}
+
+static inline void ath10k_pci_sleep(struct ath10k *ar)
+{
+       if (ath10k_target_ps)
+               ath10k_do_pci_sleep(ar);
+}
+
+#endif /* _PCI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
new file mode 100644 (file)
index 0000000..bfec6c8
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RX_DESC_H_
+#define _RX_DESC_H_
+
+enum rx_attention_flags {
+       RX_ATTENTION_FLAGS_FIRST_MPDU          = 1 << 0,
+       RX_ATTENTION_FLAGS_LAST_MPDU           = 1 << 1,
+       RX_ATTENTION_FLAGS_MCAST_BCAST         = 1 << 2,
+       RX_ATTENTION_FLAGS_PEER_IDX_INVALID    = 1 << 3,
+       RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT    = 1 << 4,
+       RX_ATTENTION_FLAGS_POWER_MGMT          = 1 << 5,
+       RX_ATTENTION_FLAGS_NON_QOS             = 1 << 6,
+       RX_ATTENTION_FLAGS_NULL_DATA           = 1 << 7,
+       RX_ATTENTION_FLAGS_MGMT_TYPE           = 1 << 8,
+       RX_ATTENTION_FLAGS_CTRL_TYPE           = 1 << 9,
+       RX_ATTENTION_FLAGS_MORE_DATA           = 1 << 10,
+       RX_ATTENTION_FLAGS_EOSP                = 1 << 11,
+       RX_ATTENTION_FLAGS_U_APSD_TRIGGER      = 1 << 12,
+       RX_ATTENTION_FLAGS_FRAGMENT            = 1 << 13,
+       RX_ATTENTION_FLAGS_ORDER               = 1 << 14,
+       RX_ATTENTION_FLAGS_CLASSIFICATION      = 1 << 15,
+       RX_ATTENTION_FLAGS_OVERFLOW_ERR        = 1 << 16,
+       RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR     = 1 << 17,
+       RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18,
+       RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL      = 1 << 19,
+       RX_ATTENTION_FLAGS_SA_IDX_INVALID      = 1 << 20,
+       RX_ATTENTION_FLAGS_DA_IDX_INVALID      = 1 << 21,
+       RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT      = 1 << 22,
+       RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT      = 1 << 23,
+       RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED    = 1 << 24,
+       RX_ATTENTION_FLAGS_DIRECTED            = 1 << 25,
+       RX_ATTENTION_FLAGS_BUFFER_FRAGMENT     = 1 << 26,
+       RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR     = 1 << 27,
+       RX_ATTENTION_FLAGS_TKIP_MIC_ERR        = 1 << 28,
+       RX_ATTENTION_FLAGS_DECRYPT_ERR         = 1 << 29,
+       RX_ATTENTION_FLAGS_FCS_ERR             = 1 << 30,
+       RX_ATTENTION_FLAGS_MSDU_DONE           = 1 << 31,
+};
+
+struct rx_attention {
+       __le32 flags; /* %RX_ATTENTION_FLAGS_ */
+} __packed;
+
+/*
+ * first_mpdu
+ *             Indicates the first MSDU of the PPDU.  If both first_mpdu
+ *             and last_mpdu are set in the MSDU then this is a not an
+ *             A-MPDU frame but a stand alone MPDU.  Interior MPDU in an
+ *             A-MPDU shall have both first_mpdu and last_mpdu bits set to
+ *             0.  The PPDU start status will only be valid when this bit
+ *             is set.
+ *
+ * last_mpdu
+ *             Indicates the last MSDU of the last MPDU of the PPDU.  The
+ *             PPDU end status will only be valid when this bit is set.
+ *
+ * mcast_bcast
+ *             Multicast / broadcast indicator.  Only set when the MAC
+ *             address 1 bit 0 is set indicating mcast/bcast and the BSSID
+ *             matches one of the 4 BSSID registers. Only set when
+ *             first_msdu is set.
+ *
+ * peer_idx_invalid
+ *             Indicates no matching entries within the the max search
+ *             count.  Only set when first_msdu is set.
+ *
+ * peer_idx_timeout
+ *             Indicates an unsuccessful search for the peer index due to
+ *             timeout.  Only set when first_msdu is set.
+ *
+ * power_mgmt
+ *             Power management bit set in the 802.11 header.  Only set
+ *             when first_msdu is set.
+ *
+ * non_qos
+ *             Set if packet is not a non-QoS data frame.  Only set when
+ *             first_msdu is set.
+ *
+ * null_data
+ *             Set if frame type indicates either null data or QoS null
+ *             data format.  Only set when first_msdu is set.
+ *
+ * mgmt_type
+ *             Set if packet is a management packet.  Only set when
+ *             first_msdu is set.
+ *
+ * ctrl_type
+ *             Set if packet is a control packet.  Only set when first_msdu
+ *             is set.
+ *
+ * more_data
+ *             Set if more bit in frame control is set.  Only set when
+ *             first_msdu is set.
+ *
+ * eosp
+ *             Set if the EOSP (end of service period) bit in the QoS
+ *             control field is set.  Only set when first_msdu is set.
+ *
+ * u_apsd_trigger
+ *             Set if packet is U-APSD trigger.  Key table will have bits
+ *             per TID to indicate U-APSD trigger.
+ *
+ * fragment
+ *             Indicates that this is an 802.11 fragment frame.  This is
+ *             set when either the more_frag bit is set in the frame
+ *             control or the fragment number is not zero.  Only set when
+ *             first_msdu is set.
+ *
+ * order
+ *             Set if the order bit in the frame control is set.  Only set
+ *             when first_msdu is set.
+ *
+ * classification
+ *             Indicates that this status has a corresponding MSDU that
+ *             requires FW processing.  The OLE will have classification
+ *             ring mask registers which will indicate the ring(s) for
+ *             packets and descriptors which need FW attention.
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * msdu_length_err
+ *             Indicates that the MSDU length from the 802.3 encapsulated
+ *             length field extends beyond the MPDU boundary.
+ *
+ * tcp_udp_chksum_fail
+ *             Indicates that the computed checksum (tcp_udp_chksum) did
+ *             not match the checksum in the TCP/UDP header.
+ *
+ * ip_chksum_fail
+ *             Indicates that the computed checksum did not match the
+ *             checksum in the IP header.
+ *
+ * sa_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the source MAC address.
+ *
+ * da_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the destination MAC address.
+ *
+ * sa_idx_timeout
+ *             Indicates an unsuccessful search for the source MAC address
+ *             due to the expiring of the search timer.
+ *
+ * da_idx_timeout
+ *             Indicates an unsuccessful search for the destination MAC
+ *             address due to the expiring of the search timer.
+ *
+ * encrypt_required
+ *             Indicates that this data type frame is not encrypted even if
+ *             the policy for this MPDU requires encryption as indicated in
+ *             the peer table key type.
+ *
+ * directed
+ *             MPDU is a directed packet which means that the RA matched
+ *             our STA addresses.  In proxySTA it means that the TA matched
+ *             an entry in our address search table with the corresponding
+ *             'no_ack' bit is the address search entry cleared.
+ *
+ * buffer_fragment
+ *             Indicates that at least one of the rx buffers has been
+ *             fragmented.  If set the FW should look at the rx_frag_info
+ *             descriptor described below.
+ *
+ * mpdu_length_err
+ *             Indicates that the MPDU was pre-maturely terminated
+ *             resulting in a truncated MPDU.  Don't trust the MPDU length
+ *             field.
+ *
+ * tkip_mic_err
+ *             Indicates that the MPDU Michael integrity check failed
+ *
+ * decrypt_err
+ *             Indicates that the MPDU decrypt integrity check failed
+ *
+ * fcs_err
+ *             Indicates that the MPDU FCS check failed
+ *
+ * msdu_done
+ *             If set indicates that the RX packet data, RX header data, RX
+ *             PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU
+ *             start/end descriptors and RX Attention descriptor are all
+ *             valid.  This bit must be in the last octet of the
+ *             descriptor.
+ */
+
+struct rx_frag_info {
+       u8 ring0_more_count;
+       u8 ring1_more_count;
+       u8 ring2_more_count;
+       u8 ring3_more_count;
+} __packed;
+
+/*
+ * ring0_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 0.  Field is filled in by the RX_DMA.
+ *
+ * ring1_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 1. Field is filled in by the RX_DMA.
+ *
+ * ring2_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 2. Field is filled in by the RX_DMA.
+ *
+ * ring3_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 3. Field is filled in by the RX_DMA.
+ */
+
+enum htt_rx_mpdu_encrypt_type {
+       HTT_RX_MPDU_ENCRYPT_WEP40            = 0,
+       HTT_RX_MPDU_ENCRYPT_WEP104           = 1,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2,
+       HTT_RX_MPDU_ENCRYPT_WEP128           = 3,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WPA         = 4,
+       HTT_RX_MPDU_ENCRYPT_WAPI             = 5,
+       HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2     = 6,
+       HTT_RX_MPDU_ENCRYPT_NONE             = 7,
+};
+
+#define RX_MPDU_START_INFO0_PEER_IDX_MASK     0x000007ff
+#define RX_MPDU_START_INFO0_PEER_IDX_LSB      0
+#define RX_MPDU_START_INFO0_SEQ_NUM_MASK      0x0fff0000
+#define RX_MPDU_START_INFO0_SEQ_NUM_LSB       16
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB  28
+#define RX_MPDU_START_INFO0_FROM_DS           (1 << 11)
+#define RX_MPDU_START_INFO0_TO_DS             (1 << 12)
+#define RX_MPDU_START_INFO0_ENCRYPTED         (1 << 13)
+#define RX_MPDU_START_INFO0_RETRY             (1 << 14)
+#define RX_MPDU_START_INFO0_TXBF_H_INFO       (1 << 15)
+
+#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000
+#define RX_MPDU_START_INFO1_TID_LSB  28
+#define RX_MPDU_START_INFO1_DIRECTED (1 << 16)
+
+struct rx_mpdu_start {
+       __le32 info0;
+       union {
+               struct {
+                       __le32 pn31_0;
+                       __le32 info1; /* %RX_MPDU_START_INFO1_ */
+               } __packed;
+               struct {
+                       u8 pn[6];
+               } __packed;
+       } __packed;
+} __packed;
+
+/*
+ * peer_idx
+ *             The index of the address search table which associated with
+ *             the peer table entry corresponding to this MPDU.  Only valid
+ *             when first_msdu is set.
+ *
+ * fr_ds
+ *             Set if the from DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * to_ds
+ *             Set if the to DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * encrypted
+ *             Protected bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * retry
+ *             Retry bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * txbf_h_info
+ *             The MPDU data will contain H information.  Primarily used
+ *             for debug.
+ *
+ * seq_num
+ *             The sequence number from the 802.11 header.  Only valid when
+ *             first_msdu is set.
+ *
+ * encrypt_type
+ *             Indicates type of decrypt cipher used (as defined in the
+ *             peer table)
+ *             0: WEP40
+ *             1: WEP104
+ *             2: TKIP without MIC
+ *             3: WEP128
+ *             4: TKIP (WPA)
+ *             5: WAPI
+ *             6: AES-CCM (WPA2)
+ *             7: No cipher
+ *             Only valid when first_msdu_is set
+ *
+ * pn_31_0
+ *             Bits [31:0] of the PN number extracted from the IV field
+ *             WEP: IV = {key_id_octet, pn2, pn1, pn0}.  Only pn[23:0] is
+ *             valid.
+ *             TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0,
+ *             WEPSeed[1], pn1}.  Only pn[47:0] is valid.
+ *             AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1,
+ *             pn0}.  Only pn[47:0] is valid.
+ *             WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11,
+ *             pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}.
+ *             The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and
+ *             pn[47:0] are valid.
+ *             Only valid when first_msdu is set.
+ *
+ * pn_47_32
+ *             Bits [47:32] of the PN number.   See description for
+ *             pn_31_0.  The remaining PN fields are in the rx_msdu_end
+ *             descriptor
+ *
+ * pn
+ *             Use this field to access the pn without worrying about
+ *             byte-order and bitmasking/bitshifting.
+ *
+ * directed
+ *             See definition in RX attention descriptor
+ *
+ * reserved_2
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * tid
+ *             The TID field in the QoS control field
+ */
+
+#define RX_MPDU_END_INFO0_RESERVED_0_MASK     0x00001fff
+#define RX_MPDU_END_INFO0_RESERVED_0_LSB      0
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB  16
+#define RX_MPDU_END_INFO0_OVERFLOW_ERR        (1 << 13)
+#define RX_MPDU_END_INFO0_LAST_MPDU           (1 << 14)
+#define RX_MPDU_END_INFO0_POST_DELIM_ERR      (1 << 15)
+#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR     (1 << 28)
+#define RX_MPDU_END_INFO0_TKIP_MIC_ERR        (1 << 29)
+#define RX_MPDU_END_INFO0_DECRYPT_ERR         (1 << 30)
+#define RX_MPDU_END_INFO0_FCS_ERR             (1 << 31)
+
+struct rx_mpdu_end {
+       __le32 info0;
+} __packed;
+
+/*
+ * reserved_0
+ *             Reserved
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * last_mpdu
+ *             Indicates that this is the last MPDU of a PPDU.
+ *
+ * post_delim_err
+ *             Indicates that a delimiter FCS error occurred after this
+ *             MPDU before the next MPDU.  Only valid when last_msdu is
+ *             set.
+ *
+ * post_delim_cnt
+ *             Count of the delimiters after this MPDU.  This requires the
+ *             last MPDU to be held until all the EOF descriptors have been
+ *             received.  This may be inefficient in the future when
+ *             ML-MIMO is used.  Only valid when last_mpdu is set.
+ *
+ * mpdu_length_err
+ *             See definition in RX attention descriptor
+ *
+ * tkip_mic_err
+ *             See definition in RX attention descriptor
+ *
+ * decrypt_err
+ *             See definition in RX attention descriptor
+ *
+ * fcs_err
+ *             See definition in RX attention descriptor
+ */
+
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK    0x00003fff
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB     0
+#define RX_MSDU_START_INFO0_IP_OFFSET_MASK      0x000fc000
+#define RX_MSDU_START_INFO0_IP_OFFSET_LSB       14
+#define RX_MSDU_START_INFO0_RING_MASK_MASK      0x00f00000
+#define RX_MSDU_START_INFO0_RING_MASK_LSB       20
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB  24
+
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK    0x000000ff
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB     0
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK   0x00000300
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB    8
+#define RX_MSDU_START_INFO1_SA_IDX_MASK         0x07ff0000
+#define RX_MSDU_START_INFO1_SA_IDX_LSB          16
+#define RX_MSDU_START_INFO1_IPV4_PROTO          (1 << 10)
+#define RX_MSDU_START_INFO1_IPV6_PROTO          (1 << 11)
+#define RX_MSDU_START_INFO1_TCP_PROTO           (1 << 12)
+#define RX_MSDU_START_INFO1_UDP_PROTO           (1 << 13)
+#define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
+#define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
+
+enum rx_msdu_decap_format {
+       RX_MSDU_DECAP_RAW           = 0,
+       RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+       RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+       RX_MSDU_DECAP_8023_SNAP_LLC = 3
+};
+
+struct rx_msdu_start {
+       __le32 info0; /* %RX_MSDU_START_INFO0_ */
+       __le32 flow_id_crc;
+       __le32 info1; /* %RX_MSDU_START_INFO1_ */
+} __packed;
+
+/*
+ * msdu_length
+ *             MSDU length in bytes after decapsulation.  This field is
+ *             still valid for MPDU frames without A-MSDU.  It still
+ *             represents MSDU length after decapsulation
+ *
+ * ip_offset
+ *             Indicates the IP offset in bytes from the start of the
+ *             packet after decapsulation.  Only valid if ipv4_proto or
+ *             ipv6_proto is set.
+ *
+ * ring_mask
+ *             Indicates the destination RX rings for this MSDU.
+ *
+ * tcp_udp_offset
+ *             Indicates the offset in bytes to the start of TCP or UDP
+ *             header from the start of the IP header after decapsulation.
+ *             Only valid if tcp_prot or udp_prot is set.  The value 0
+ *             indicates that the offset is longer than 127 bytes.
+ *
+ * reserved_0c
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * flow_id_crc
+ *             The flow_id_crc runs CRC32 on the following information:
+ *             IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0,
+ *             protocol[7:0]}.
+ *             IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0,
+ *             next_header[7:0]}
+ *             UDP case: sort_port[15:0], dest_port[15:0]
+ *             TCP case: sort_port[15:0], dest_port[15:0],
+ *             {header_length[3:0], 6'b0, flags[5:0], window_size[15:0]},
+ *             {16'b0, urgent_ptr[15:0]}, all options except 32-bit
+ *             timestamp.
+ *
+ * msdu_number
+ *             Indicates the MSDU number within a MPDU.  This value is
+ *             reset to zero at the start of each MPDU.  If the number of
+ *             MSDU exceeds 255 this number will wrap using modulo 256.
+ *
+ * decap_format
+ *             Indicates the format after decapsulation:
+ *             0: RAW: No decapsulation
+ *             1: Native WiFi
+ *             2: Ethernet 2 (DIX)
+ *             3: 802.3 (SNAP/LLC)
+ *
+ * ipv4_proto
+ *             Set if L2 layer indicates IPv4 protocol.
+ *
+ * ipv6_proto
+ *             Set if L2 layer indicates IPv6 protocol.
+ *
+ * tcp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *             protocol indicates TCP.
+ *
+ * udp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *                     protocol indicates UDP.
+ *
+ * ip_frag
+ *             Indicates that either the IP More frag bit is set or IP frag
+ *             number is non-zero.  If set indicates that this is a
+ *             fragmented IP packet.
+ *
+ * tcp_only_ack
+ *             Set if only the TCP Ack bit is set in the TCP flags and if
+ *             the TCP payload is 0.
+ *
+ * sa_idx
+ *             The offset in the address table which matches the MAC source
+ *             address.
+ *
+ * reserved_2b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB  0
+#define RX_MSDU_END_INFO0_FIRST_MSDU                (1 << 14)
+#define RX_MSDU_END_INFO0_LAST_MSDU                 (1 << 15)
+#define RX_MSDU_END_INFO0_PRE_DELIM_ERR             (1 << 30)
+#define RX_MSDU_END_INFO0_RESERVED_3B               (1 << 31)
+
+struct rx_msdu_end {
+       __le16 ip_hdr_cksum;
+       __le16 tcp_hdr_cksum;
+       u8 key_id_octet;
+       u8 classification_filter;
+       u8 wapi_pn[10];
+       __le32 info0;
+} __packed;
+
+/*
+ *ip_hdr_chksum
+ *             This can include the IP header checksum or the pseudo header
+ *             checksum used by TCP/UDP checksum.
+ *
+ *tcp_udp_chksum
+ *             The value of the computed TCP/UDP checksum.  A mode bit
+ *             selects whether this checksum is the full checksum or the
+ *             partial checksum which does not include the pseudo header.
+ *
+ *key_id_octet
+ *             The key ID octet from the IV.  Only valid when first_msdu is
+ *             set.
+ *
+ *classification_filter
+ *             Indicates the number classification filter rule
+ *
+ *ext_wapi_pn_63_48
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [63:48] (pn6 and pn7).  The
+ *             WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start
+ *             descriptor.
+ *
+ *ext_wapi_pn_95_64
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and
+ *             pn11).
+ *
+ *ext_wapi_pn_127_96
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14,
+ *             pn15).
+ *
+ *reported_mpdu_length
+ *             MPDU length before decapsulation.  Only valid when
+ *             first_msdu is set.  This field is taken directly from the
+ *             length field of the A-MPDU delimiter or the preamble length
+ *             field for non-A-MPDU frames.
+ *
+ *first_msdu
+ *             Indicates the first MSDU of A-MSDU.  If both first_msdu and
+ *             last_msdu are set in the MSDU then this is a non-aggregated
+ *             MSDU frame: normal MPDU.  Interior MSDU in an A-MSDU shall
+ *             have both first_mpdu and last_mpdu bits set to 0.
+ *
+ *last_msdu
+ *             Indicates the last MSDU of the A-MSDU.  MPDU end status is
+ *             only valid when last_msdu is set.
+ *
+ *reserved_3a
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ *pre_delim_err
+ *             Indicates that the first delimiter had a FCS failure.  Only
+ *             valid when first_mpdu and first_msdu are set.
+ *
+ *reserved_3b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0
+#define RX_PPDU_START_SIG_RATE_SELECT_CCK  1
+
+#define RX_PPDU_START_SIG_RATE_OFDM_48 0
+#define RX_PPDU_START_SIG_RATE_OFDM_24 1
+#define RX_PPDU_START_SIG_RATE_OFDM_12 2
+#define RX_PPDU_START_SIG_RATE_OFDM_6  3
+#define RX_PPDU_START_SIG_RATE_OFDM_54 4
+#define RX_PPDU_START_SIG_RATE_OFDM_36 5
+#define RX_PPDU_START_SIG_RATE_OFDM_18 6
+#define RX_PPDU_START_SIG_RATE_OFDM_9  7
+
+#define RX_PPDU_START_SIG_RATE_CCK_LP_11  0
+#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1
+#define RX_PPDU_START_SIG_RATE_CCK_LP_2   2
+#define RX_PPDU_START_SIG_RATE_CCK_LP_1   3
+#define RX_PPDU_START_SIG_RATE_CCK_SP_11  4
+#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5
+#define RX_PPDU_START_SIG_RATE_CCK_SP_2   6
+
+#define HTT_RX_PPDU_START_PREAMBLE_LEGACY        0x04
+#define HTT_RX_PPDU_START_PREAMBLE_HT            0x08
+#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF  0x09
+#define HTT_RX_PPDU_START_PREAMBLE_VHT           0x0C
+#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D
+
+#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0)
+
+#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK    0x0000000f
+#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB     0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK  0x0001ffe0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB   5
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK    0x00fc0000
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB     18
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB  24
+#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT  (1 << 4)
+#define RX_PPDU_START_INFO1_L_SIG_PARITY       (1 << 17)
+
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB  0
+
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB  0
+#define RX_PPDU_START_INFO3_TXBF_H_INFO             (1 << 24)
+
+#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff
+#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB  0
+
+#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff
+#define RX_PPDU_START_INFO5_SERVICE_LSB  0
+
+struct rx_ppdu_start {
+       struct {
+               u8 pri20_mhz;
+               u8 ext20_mhz;
+               u8 ext40_mhz;
+               u8 ext80_mhz;
+       } rssi_chains[4];
+       u8 rssi_comb;
+       __le16 rsvd0;
+       u8 info0; /* %RX_PPDU_START_INFO0_ */
+       __le32 info1; /* %RX_PPDU_START_INFO1_ */
+       __le32 info2; /* %RX_PPDU_START_INFO2_ */
+       __le32 info3; /* %RX_PPDU_START_INFO3_ */
+       __le32 info4; /* %RX_PPDU_START_INFO4_ */
+       __le32 info5; /* %RX_PPDU_START_INFO5_ */
+} __packed;
+
+/*
+ * rssi_chain0_pri20
+ *             RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec20
+ *             RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec40
+ *             RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec80
+ *             RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_pri20
+ *             RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec20
+ *             RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec40
+ *             RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec80
+ *             RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_pri20
+ *             RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec20
+ *             RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec40
+ *             RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec80
+ *             RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_pri20
+ *             RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec20
+ *             RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec40
+ *             RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec80
+ *             RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_comb
+ *             The combined RSSI of RX PPDU of all active chains and
+ *             bandwidths.  Value of 0x80 indicates invalid.
+ *
+ * reserved_4a
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * is_greenfield
+ *             Do we really support this?
+ *
+ * reserved_4b
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * l_sig_rate
+ *             If l_sig_rate_select is 0:
+ *             0x8: OFDM 48 Mbps
+ *             0x9: OFDM 24 Mbps
+ *             0xA: OFDM 12 Mbps
+ *             0xB: OFDM 6 Mbps
+ *             0xC: OFDM 54 Mbps
+ *             0xD: OFDM 36 Mbps
+ *             0xE: OFDM 18 Mbps
+ *             0xF: OFDM 9 Mbps
+ *             If l_sig_rate_select is 1:
+ *             0x8: CCK 11 Mbps long preamble
+ *             0x9: CCK 5.5 Mbps long preamble
+ *             0xA: CCK 2 Mbps long preamble
+ *             0xB: CCK 1 Mbps long preamble
+ *             0xC: CCK 11 Mbps short preamble
+ *             0xD: CCK 5.5 Mbps short preamble
+ *             0xE: CCK 2 Mbps short preamble
+ *
+ * l_sig_rate_select
+ *             Legacy signal rate select.  If set then l_sig_rate indicates
+ *             CCK rates.  If clear then l_sig_rate indicates OFDM rates.
+ *
+ * l_sig_length
+ *             Length of legacy frame in octets.
+ *
+ * l_sig_parity
+ *             Odd parity over l_sig_rate and l_sig_length
+ *
+ * l_sig_tail
+ *             Tail bits for Viterbi decoder
+ *
+ * preamble_type
+ *             Indicates the type of preamble ahead:
+ *             0x4: Legacy (OFDM/CCK)
+ *             0x8: HT
+ *             0x9: HT with TxBF
+ *             0xC: VHT
+ *             0xD: VHT with TxBF
+ *             0x80 - 0xFF: Reserved for special baseband data types such
+ *             as radar and spectral scan.
+ *
+ * ht_sig_vht_sig_a_1
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (first 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (first 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * reserved_6
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ht_sig_vht_sig_a_2
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (last 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (last 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_7
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * vht_sig_b
+ *             WiFi 1.0 and WiFi 2.0 will likely have this field to be all
+ *             0s since the BB does not plan on decoding VHT SIG-B.
+ *
+ * reserved_8
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * service
+ *             Service field from BB for OFDM, HT and VHT packets.  CCK
+ *             packets will have service field of 0.
+ *
+ * reserved_9
+ *             Reserved: HW should fill with 0, FW should ignore.
+*/
+
+
+#define RX_PPDU_END_FLAGS_PHY_ERR             (1 << 0)
+#define RX_PPDU_END_FLAGS_RX_LOCATION         (1 << 1)
+#define RX_PPDU_END_FLAGS_TXBF_H_INFO         (1 << 2)
+
+#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK     0x00ffffff
+#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB      0
+#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24)
+#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25)
+
+#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
+
+struct rx_ppdu_end {
+       __le32 evm_p0;
+       __le32 evm_p1;
+       __le32 evm_p2;
+       __le32 evm_p3;
+       __le32 evm_p4;
+       __le32 evm_p5;
+       __le32 evm_p6;
+       __le32 evm_p7;
+       __le32 evm_p8;
+       __le32 evm_p9;
+       __le32 evm_p10;
+       __le32 evm_p11;
+       __le32 evm_p12;
+       __le32 evm_p13;
+       __le32 evm_p14;
+       __le32 evm_p15;
+       __le32 tsf_timestamp;
+       __le32 wb_timestamp;
+       u8 locationing_timestamp;
+       u8 phy_err_code;
+       __le16 flags; /* %RX_PPDU_END_FLAGS_ */
+       __le32 info0; /* %RX_PPDU_END_INFO0_ */
+       __le16 bb_length;
+       __le16 info1; /* %RX_PPDU_END_INFO1_ */
+} __packed;
+
+/*
+ * evm_p0
+ *             EVM for pilot 0.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p1
+ *             EVM for pilot 1.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p2
+ *             EVM for pilot 2.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p3
+ *             EVM for pilot 3.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p4
+ *             EVM for pilot 4.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p5
+ *             EVM for pilot 5.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p6
+ *             EVM for pilot 6.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p7
+ *             EVM for pilot 7.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p8
+ *             EVM for pilot 8.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p9
+ *             EVM for pilot 9.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p10
+ *             EVM for pilot 10.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p11
+ *             EVM for pilot 11.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p12
+ *             EVM for pilot 12.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p13
+ *             EVM for pilot 13.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p14
+ *             EVM for pilot 14.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p15
+ *             EVM for pilot 15.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * tsf_timestamp
+ *             Receive TSF timestamp sampled on the rising edge of
+ *             rx_clear.  For PHY errors this may be the current TSF when
+ *             phy_error is asserted if the rx_clear does not assert before
+ *             the end of the PHY error.
+ *
+ * wb_timestamp
+ *             WLAN/BT timestamp is a 1 usec resolution timestamp which
+ *             does not get updated based on receive beacon like TSF.  The
+ *             same rules for capturing tsf_timestamp are used to capture
+ *             the wb_timestamp.
+ *
+ * locationing_timestamp
+ *             Timestamp used for locationing.  This timestamp is used to
+ *             indicate fractions of usec.  For example if the MAC clock is
+ *             running at 80 MHz, the timestamp will increment every 12.5
+ *             nsec.  The value starts at 0 and increments to 79 and
+ *             returns to 0 and repeats.  This information is valid for
+ *             every PPDU.  This information can be used in conjunction
+ *             with wb_timestamp to capture large delta times.
+ *
+ * phy_err_code
+ *             See the 1.10.8.1.2 for the list of the PHY error codes.
+ *
+ * phy_err
+ *             Indicates a PHY error was detected for this PPDU.
+ *
+ * rx_location
+ *             Indicates that location information was requested.
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_18
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * rx_antenna
+ *             Receive antenna value
+ *
+ * tx_ht_vht_ack
+ *             Indicates that a HT or VHT Ack/BA frame was transmitted in
+ *             response to this receive packet.
+ *
+ * bb_captured_channel
+ *             Indicates that the BB has captured a channel dump.  FW can
+ *             then read the channel dump memory.  This may indicate that
+ *             the channel was captured either based on PCU setting the
+ *             capture_channel bit  BB descriptor or FW setting the
+ *             capture_channel mode bit.
+ *
+ * reserved_19
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * bb_length
+ *             Indicates the number of bytes of baseband information for
+ *             PPDUs where the BB descriptor preamble type is 0x80 to 0xFF
+ *             which indicates that this is not a normal PPDU but rather
+ *             contains baseband debug information.
+ *
+ * reserved_20
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ppdu_done
+ *             PPDU end status is only valid when ppdu_done bit is set.
+ *             Every time HW sets this bit in memory FW/SW must clear this
+ *             bit in memory.  FW will initialize all the ppdu_done dword
+ *             to 0.
+*/
+
+#define FW_RX_DESC_INFO0_DISCARD  (1 << 0)
+#define FW_RX_DESC_INFO0_FORWARD  (1 << 1)
+#define FW_RX_DESC_INFO0_INSPECT  (1 << 5)
+#define FW_RX_DESC_INFO0_EXT_MASK 0xC0
+#define FW_RX_DESC_INFO0_EXT_LSB  6
+
+struct fw_rx_desc_base {
+       u8 info0;
+} __packed;
+
+#endif /* _RX_DESC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
new file mode 100644 (file)
index 0000000..be7ba1e
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __TARGADDRS_H__
+#define __TARGADDRS_H__
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.  It must match the address of the _host_interest
+ * symbol (see linker script).
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end) across software releases.
+ *
+ * All addresses are available here so that it's possible to
+ * write a single binary that works with all Target Types.
+ * May be used in assembler code as well as C.
+ */
+#define QCA988X_HOST_INTEREST_ADDRESS    0x00400800
+#define HOST_INTEREST_MAX_SIZE          0x200
+
+/*
+ * These are items that the Host may need to access via BMI or via the
+ * Diagnostic Window. The position of items in this structure must remain
+ * constant across firmware revisions! Types for each item must be fixed
+ * size across target and host platforms. More items may be added at the end.
+ */
+struct host_interest {
+       /*
+        * Pointer to application-defined area, if any.
+        * Set by Target application during startup.
+        */
+       u32 hi_app_host_interest;                       /* 0x00 */
+
+       /* Pointer to register dump area, valid after Target crash. */
+       u32 hi_failure_state;                           /* 0x04 */
+
+       /* Pointer to debug logging header */
+       u32 hi_dbglog_hdr;                              /* 0x08 */
+
+       u32 hi_unused0c;                                /* 0x0c */
+
+       /*
+        * General-purpose flag bits, similar to SOC_OPTION_* flags.
+        * Can be used by application rather than by OS.
+        */
+       u32 hi_option_flag;                             /* 0x10 */
+
+       /*
+        * Boolean that determines whether or not to
+        * display messages on the serial port.
+        */
+       u32 hi_serial_enable;                           /* 0x14 */
+
+       /* Start address of DataSet index, if any */
+       u32 hi_dset_list_head;                          /* 0x18 */
+
+       /* Override Target application start address */
+       u32 hi_app_start;                               /* 0x1c */
+
+       /* Clock and voltage tuning */
+       u32 hi_skip_clock_init;                         /* 0x20 */
+       u32 hi_core_clock_setting;                      /* 0x24 */
+       u32 hi_cpu_clock_setting;                       /* 0x28 */
+       u32 hi_system_sleep_setting;                    /* 0x2c */
+       u32 hi_xtal_control_setting;                    /* 0x30 */
+       u32 hi_pll_ctrl_setting_24ghz;                  /* 0x34 */
+       u32 hi_pll_ctrl_setting_5ghz;                   /* 0x38 */
+       u32 hi_ref_voltage_trim_setting;                /* 0x3c */
+       u32 hi_clock_info;                              /* 0x40 */
+
+       /* Host uses BE CPU or not */
+       u32 hi_be;                                      /* 0x44 */
+
+       u32 hi_stack;   /* normal stack */                      /* 0x48 */
+       u32 hi_err_stack; /* error stack */             /* 0x4c */
+       u32 hi_desired_cpu_speed_hz;                    /* 0x50 */
+
+       /* Pointer to Board Data  */
+       u32 hi_board_data;                              /* 0x54 */
+
+       /*
+        * Indication of Board Data state:
+        *    0: board data is not yet initialized.
+        *    1: board data is initialized; unknown size
+        *   >1: number of bytes of initialized board data
+        */
+       u32 hi_board_data_initialized;                  /* 0x58 */
+
+       u32 hi_dset_ram_index_table;                    /* 0x5c */
+
+       u32 hi_desired_baud_rate;                       /* 0x60 */
+       u32 hi_dbglog_config;                           /* 0x64 */
+       u32 hi_end_ram_reserve_sz;                      /* 0x68 */
+       u32 hi_mbox_io_block_sz;                        /* 0x6c */
+
+       u32 hi_num_bpatch_streams;                      /* 0x70 -- unused */
+       u32 hi_mbox_isr_yield_limit;                    /* 0x74 */
+
+       u32 hi_refclk_hz;                               /* 0x78 */
+       u32 hi_ext_clk_detected;                        /* 0x7c */
+       u32 hi_dbg_uart_txpin;                          /* 0x80 */
+       u32 hi_dbg_uart_rxpin;                          /* 0x84 */
+       u32 hi_hci_uart_baud;                           /* 0x88 */
+       u32 hi_hci_uart_pin_assignments;                /* 0x8C */
+
+       u32 hi_hci_uart_baud_scale_val;                 /* 0x90 */
+       u32 hi_hci_uart_baud_step_val;                  /* 0x94 */
+
+       u32 hi_allocram_start;                          /* 0x98 */
+       u32 hi_allocram_sz;                             /* 0x9c */
+       u32 hi_hci_bridge_flags;                        /* 0xa0 */
+       u32 hi_hci_uart_support_pins;                   /* 0xa4 */
+
+       u32 hi_hci_uart_pwr_mgmt_params;                /* 0xa8 */
+
+       /*
+        * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
+        *        [31:16]: wakeup timeout in ms
+        */
+       /* Pointer to extended board Data  */
+       u32 hi_board_ext_data;                          /* 0xac */
+       u32 hi_board_ext_data_config;                   /* 0xb0 */
+       /*
+        * Bit [0]  :   valid
+        * Bit[31:16:   size
+        */
+       /*
+        * hi_reset_flag is used to do some stuff when target reset.
+        * such as restore app_start after warm reset or
+        * preserve host Interest area, or preserve ROM data, literals etc.
+        */
+       u32  hi_reset_flag;                             /* 0xb4 */
+       /* indicate hi_reset_flag is valid */
+       u32  hi_reset_flag_valid;                       /* 0xb8 */
+       u32 hi_hci_uart_pwr_mgmt_params_ext;            /* 0xbc */
+       /* 0xbc - [31:0]: idle timeout in ms */
+       /* ACS flags */
+       u32 hi_acs_flags;                               /* 0xc0 */
+       u32 hi_console_flags;                           /* 0xc4 */
+       u32 hi_nvram_state;                             /* 0xc8 */
+       u32 hi_option_flag2;                            /* 0xcc */
+
+       /* If non-zero, override values sent to Host in WMI_READY event. */
+       u32 hi_sw_version_override;                     /* 0xd0 */
+       u32 hi_abi_version_override;                    /* 0xd4 */
+
+       /*
+        * Percentage of high priority RX traffic to total expected RX traffic
+        * applicable only to ar6004
+        */
+       u32 hi_hp_rx_traffic_ratio;                     /* 0xd8 */
+
+       /* test applications flags */
+       u32 hi_test_apps_related;                       /* 0xdc */
+       /* location of test script */
+       u32 hi_ota_testscript;                          /* 0xe0 */
+       /* location of CAL data */
+       u32 hi_cal_data;                                /* 0xe4 */
+
+       /* Number of packet log buffers */
+       u32 hi_pktlog_num_buffers;                      /* 0xe8 */
+
+       /* wow extension configuration */
+       u32 hi_wow_ext_config;                          /* 0xec */
+       u32 hi_pwr_save_flags;                          /* 0xf0 */
+
+       /* Spatial Multiplexing Power Save (SMPS) options */
+       u32 hi_smps_options;                            /* 0xf4 */
+
+       /* Interconnect-specific state */
+       u32 hi_interconnect_state;                      /* 0xf8 */
+
+       /* Coex configuration flags */
+       u32 hi_coex_config;                             /* 0xfc */
+
+       /* Early allocation support */
+       u32 hi_early_alloc;                             /* 0x100 */
+       /* FW swap field */
+       /*
+        * Bits of this 32bit word will be used to pass specific swap
+        * instruction to FW
+        */
+       /*
+        * Bit 0 -- AP Nart descriptor no swap. When this bit is set
+        * FW will not swap TX descriptor. Meaning packets are formed
+        * on the target processor.
+        */
+       /* Bit 1 - unused */
+       u32 hi_fw_swap;                                 /* 0x104 */
+} __packed;
+
+#define HI_ITEM(item)  offsetof(struct host_interest, item)
+
+/* Bits defined in hi_option_flag */
+
+/* Enable timer workaround */
+#define HI_OPTION_TIMER_WAR         0x01
+/* Limit BMI command credits */
+#define HI_OPTION_BMI_CRED_LIMIT    0x02
+/* Relay Dot11 hdr to/from host */
+#define HI_OPTION_RELAY_DOT11_HDR   0x04
+/* MAC addr method 0-locally administred 1-globally unique addrs */
+#define HI_OPTION_MAC_ADDR_METHOD   0x08
+/* Firmware Bridging */
+#define HI_OPTION_FW_BRIDGE         0x10
+/* Enable CPU profiling */
+#define HI_OPTION_ENABLE_PROFILE    0x20
+/* Disable debug logging */
+#define HI_OPTION_DISABLE_DBGLOG    0x40
+/* Skip Era Tracking */
+#define HI_OPTION_SKIP_ERA_TRACKING 0x80
+/* Disable PAPRD (debug) */
+#define HI_OPTION_PAPRD_DISABLE     0x100
+#define HI_OPTION_NUM_DEV_LSB       0x200
+#define HI_OPTION_NUM_DEV_MSB       0x800
+#define HI_OPTION_DEV_MODE_LSB      0x1000
+#define HI_OPTION_DEV_MODE_MSB      0x8000000
+/* Disable LowFreq Timer Stabilization */
+#define HI_OPTION_NO_LFT_STBL       0x10000000
+/* Skip regulatory scan */
+#define HI_OPTION_SKIP_REG_SCAN     0x20000000
+/*
+ * Do regulatory scan during init before
+ * sending WMI ready event to host
+ */
+#define HI_OPTION_INIT_REG_SCAN     0x40000000
+
+/* REV6: Do not adjust memory map */
+#define HI_OPTION_SKIP_MEMMAP       0x80000000
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
+
+/* 2 bits of hi_option_flag are used to represent 3 modes */
+#define HI_OPTION_FW_MODE_IBSS    0x0 /* IBSS Mode */
+#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */
+#define HI_OPTION_FW_MODE_AP      0x2 /* AP Mode */
+#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */
+
+/* 2 bits of hi_option flag are usedto represent 4 submodes */
+#define HI_OPTION_FW_SUBMODE_NONE    0x0  /* Normal mode */
+#define HI_OPTION_FW_SUBMODE_P2PDEV  0x1  /* p2p device mode */
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */
+#define HI_OPTION_FW_SUBMODE_P2PGO   0x3 /* p2p go mode */
+
+/* Num dev Mask */
+#define HI_OPTION_NUM_DEV_MASK    0x7
+#define HI_OPTION_NUM_DEV_SHIFT   0x9
+
+/* firmware bridging */
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/*
+Fw Mode/SubMode Mask
+|-----------------------------------------------------------------------------|
+|  SUB   |   SUB   |   SUB   |  SUB    |         |         |         |        |
+|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]|
+|  (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)  |
+|-----------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_BITS         0x2
+#define HI_OPTION_FW_MODE_MASK         0x3
+#define HI_OPTION_FW_MODE_SHIFT        0xC
+#define HI_OPTION_ALL_FW_MODE_MASK     0xFF
+
+#define HI_OPTION_FW_SUBMODE_BITS      0x2
+#define HI_OPTION_FW_SUBMODE_MASK      0x3
+#define HI_OPTION_FW_SUBMODE_SHIFT     0x14
+#define HI_OPTION_ALL_FW_SUBMODE_MASK  0xFF00
+#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
+
+
+/* hi_option_flag2 options */
+#define HI_OPTION_OFFLOAD_AMSDU     0x01
+#define HI_OPTION_DFS_SUPPORT       0x02 /* Enable DFS support */
+#define HI_OPTION_ENABLE_RFKILL     0x04 /* RFKill Enable Feature*/
+#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */
+#define HI_OPTION_EARLY_CFG_DONE    0x10 /* Early configuration is complete */
+
+#define HI_OPTION_RF_KILL_SHIFT     0x2
+#define HI_OPTION_RF_KILL_MASK      0x1
+
+/* hi_reset_flag */
+/* preserve App Start address */
+#define HI_RESET_FLAG_PRESERVE_APP_START         0x01
+/* preserve host interest */
+#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST     0x02
+/* preserve ROM data */
+#define HI_RESET_FLAG_PRESERVE_ROMDATA           0x04
+#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE       0x08
+#define HI_RESET_FLAG_PRESERVE_BOOT_INFO         0x10
+#define HI_RESET_FLAG_WARM_RESET       0x20
+
+/* define hi_fw_swap bits */
+#define HI_DESC_IN_FW_BIT      0x01
+
+/* indicate the reset flag is valid */
+#define HI_RESET_FLAG_IS_VALID  0x12345678
+
+/* ACS is enabled */
+#define HI_ACS_FLAGS_ENABLED        (1 << 0)
+/* Use physical WWAN device */
+#define HI_ACS_FLAGS_USE_WWAN       (1 << 1)
+/* Use test VAP */
+#define HI_ACS_FLAGS_TEST_VAP       (1 << 2)
+
+/*
+ * CONSOLE FLAGS
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   2..0     UART ID (0 = Default)
+ *    3       Baud Select (0 = 9600, 1 = 115200)
+ *   30..4    Reserved
+ *    31      Enable Console
+ *
+ */
+
+#define HI_CONSOLE_FLAGS_ENABLE       (1 << 31)
+#define HI_CONSOLE_FLAGS_UART_MASK    (0x7)
+#define HI_CONSOLE_FLAGS_UART_SHIFT   0
+#define HI_CONSOLE_FLAGS_BAUD_SELECT  (1 << 3)
+
+/* SM power save options */
+#define HI_SMPS_ALLOW_MASK            (0x00000001)
+#define HI_SMPS_MODE_MASK             (0x00000002)
+#define HI_SMPS_MODE_STATIC           (0x00000000)
+#define HI_SMPS_MODE_DYNAMIC          (0x00000002)
+#define HI_SMPS_DISABLE_AUTO_MODE     (0x00000004)
+#define HI_SMPS_DATA_THRESH_MASK      (0x000007f8)
+#define HI_SMPS_DATA_THRESH_SHIFT     (3)
+#define HI_SMPS_RSSI_THRESH_MASK      (0x0007f800)
+#define HI_SMPS_RSSI_THRESH_SHIFT     (11)
+#define HI_SMPS_LOWPWR_CM_MASK        (0x00380000)
+#define HI_SMPS_LOWPWR_CM_SHIFT       (15)
+#define HI_SMPS_HIPWR_CM_MASK         (0x03c00000)
+#define HI_SMPS_HIPWR_CM_SHIFT        (19)
+
+/*
+ * WOW Extension configuration
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   8..0     Size of each WOW pattern (max 511)
+ *   15..9    Number of patterns per list (max 127)
+ *   17..16   Number of lists (max 4)
+ *   30..18   Reserved
+ *   31       Enabled
+ *
+ *  set values (except enable) to zeros for default settings
+ */
+
+#define HI_WOW_EXT_ENABLED_MASK        (1 << 31)
+#define HI_WOW_EXT_NUM_LIST_SHIFT      16
+#define HI_WOW_EXT_NUM_LIST_MASK       (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_NUM_PATTERNS_SHIFT  9
+#define HI_WOW_EXT_NUM_PATTERNS_MASK   (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_PATTERN_SIZE_SHIFT  0
+#define HI_WOW_EXT_PATTERN_SIZE_MASK   (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \
+       ((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \
+               HI_WOW_EXT_NUM_LIST_MASK) | \
+       (((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \
+               HI_WOW_EXT_NUM_PATTERNS_MASK) | \
+       (((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \
+               HI_WOW_EXT_PATTERN_SIZE_MASK))
+
+#define HI_WOW_EXT_GET_NUM_LISTS(config) \
+       (((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \
+       (((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \
+               HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \
+       (((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \
+               HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+/*
+ * Early allocation configuration
+ * Support RAM bank configuration before BMI done and this eases the memory
+ * allocation at very early stage
+ * Bit Range  Meaning
+ * ---------  ----------------------------------
+ * [0:3]      number of bank assigned to be IRAM
+ * [4:15]     reserved
+ * [16:31]    magic number
+ *
+ * Note:
+ * 1. target firmware would check magic number and if it's a match, firmware
+ *    would consider the bits[0:15] are valid and base on that to calculate
+ *    the end of DRAM. Early allocation would be located at that area and
+ *    may be reclaimed when necesary
+ * 2. if no magic number is found, early allocation would happen at "_end"
+ *    symbol of ROM which is located before the app-data and might NOT be
+ *    re-claimable. If this is adopted, link script should keep this in
+ *    mind to avoid data corruption.
+ */
+#define HI_EARLY_ALLOC_MAGIC           0x6d8a
+#define HI_EARLY_ALLOC_MAGIC_MASK      0xffff0000
+#define HI_EARLY_ALLOC_MAGIC_SHIFT     16
+#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f
+#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT        0
+
+#define HI_EARLY_ALLOC_VALID() \
+       ((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \
+       HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC))
+#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \
+       (((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \
+       >> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
+
+/*power save flag bit definitions*/
+#define HI_PWR_SAVE_LPL_ENABLED   0x1
+/*b1-b3 reserved*/
+/*b4-b5 : dev0 LPL type : 0 - none
+                         1- Reduce Pwr Search
+                         2- Reduce Pwr Listen*/
+/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/
+#define HI_PWR_SAVE_LPL_DEV0_LSB   4
+#define HI_PWR_SAVE_LPL_DEV_MASK   0x3
+/*power save related utility macros*/
+#define HI_LPL_ENABLED() \
+       ((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED))
+#define HI_DEV_LPL_TYPE_GET(_devix) \
+       (HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \
+        (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2)))
+
+#define HOST_INTEREST_SMPS_IS_ALLOWED() \
+       ((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK))
+
+/* Reserve 1024 bytes for extended board data */
+#define QCA988X_BOARD_DATA_SZ     7168
+#define QCA988X_BOARD_EXT_DATA_SZ 0
+
+#endif /* __TARGADDRS_H__ */
diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c
new file mode 100644 (file)
index 0000000..4a31e2c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
new file mode 100644 (file)
index 0000000..85e806b
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <linux/tracepoint.h>
+
+#define _TRACE_H_
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH10K_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath10k
+
+#define ATH10K_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath10k_log_event,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath10k_log_dbg,
+       TP_PROTO(unsigned int level, struct va_format *vaf),
+       TP_ARGS(level, vaf),
+       TP_STRUCT__entry(
+               __field(unsigned int, level)
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               __entry->level = level;
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath10k_log_dbg_dump,
+       TP_PROTO(const char *msg, const char *prefix,
+                const void *buf, size_t buf_len),
+
+       TP_ARGS(msg, prefix, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __string(msg, msg)
+               __string(prefix, prefix)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __assign_str(msg, msg);
+               __assign_str(prefix, prefix);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "%s/%s\n", __get_str(prefix), __get_str(msg)
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_cmd,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_event,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
new file mode 100644 (file)
index 0000000..68b6fae
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "txrx.h"
+#include "htt.h"
+#include "mac.h"
+#include "debug.h"
+
+static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->htt.is_offchan)
+               return;
+
+       /* If the original wait_for_completion() timed out before
+        * {data,mgmt}_tx_completed() was called then we could complete
+        * offchan_tx_completed for a different skb. Prevent this by using
+        * offchan_tx_skb. */
+       spin_lock_bh(&ar->data_lock);
+       if (ar->offchan_tx_skb != skb) {
+               ath10k_warn("completed old offchannel frame\n");
+               goto out;
+       }
+
+       complete(&ar->offchan_tx_completed);
+       ar->offchan_tx_skb = NULL; /* just for sanity */
+
+       ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
+out:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
+{
+       struct device *dev = htt->ar->dev;
+       struct ieee80211_tx_info *info;
+       struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
+       struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
+       int ret;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
+               return;
+
+       ATH10K_SKB_CB(txdesc)->htt.refcount--;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+               return;
+
+       if (txfrag) {
+               ret = ath10k_skb_unmap(dev, txfrag);
+               if (ret)
+                       ath10k_warn("txfrag unmap failed (%d)\n", ret);
+
+               dev_kfree_skb_any(txfrag);
+       }
+
+       ret = ath10k_skb_unmap(dev, msdu);
+       if (ret)
+               ath10k_warn("data skb unmap failed (%d)\n", ret);
+
+       ath10k_report_offchan_tx(htt->ar, msdu);
+
+       info = IEEE80211_SKB_CB(msdu);
+       memset(&info->status, 0, sizeof(info->status));
+
+       if (ATH10K_SKB_CB(txdesc)->htt.discard) {
+               ieee80211_free_txskb(htt->ar->hw, msdu);
+               goto exit;
+       }
+
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
+               info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+       ieee80211_tx_status(htt->ar->hw, msdu);
+       /* we do not own the msdu anymore */
+
+exit:
+       spin_lock_bh(&htt->tx_lock);
+       htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+       __ath10k_htt_tx_dec_pending(htt);
+       if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
+               wake_up(&htt->empty_tx_wq);
+       spin_unlock_bh(&htt->tx_lock);
+
+       dev_kfree_skb_any(txdesc);
+}
+
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done)
+{
+       struct sk_buff *txdesc;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+                  tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
+
+       if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+               ath10k_warn("warning: msdu_id %d too big, ignoring\n",
+                           tx_done->msdu_id);
+               return;
+       }
+
+       txdesc = htt->pending_tx[tx_done->msdu_id];
+
+       ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
+       ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
+
+       ath10k_txrx_tx_unref(htt, txdesc);
+}
+
+static const u8 rx_legacy_rate_idx[] = {
+       3,      /* 0x00  - 11Mbps  */
+       2,      /* 0x01  - 5.5Mbps */
+       1,      /* 0x02  - 2Mbps   */
+       0,      /* 0x03  - 1Mbps   */
+       3,      /* 0x04  - 11Mbps  */
+       2,      /* 0x05  - 5.5Mbps */
+       1,      /* 0x06  - 2Mbps   */
+       0,      /* 0x07  - 1Mbps   */
+       10,     /* 0x08  - 48Mbps  */
+       8,      /* 0x09  - 24Mbps  */
+       6,      /* 0x0A  - 12Mbps  */
+       4,      /* 0x0B  - 6Mbps   */
+       11,     /* 0x0C  - 54Mbps  */
+       9,      /* 0x0D  - 36Mbps  */
+       7,      /* 0x0E  - 18Mbps  */
+       5,      /* 0x0F  - 9Mbps   */
+};
+
+static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
+                            enum ieee80211_band band,
+                            struct ieee80211_rx_status *status)
+{
+       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+       u8 info0 = info->rate.info0;
+       u32 info1 = info->rate.info1;
+       u32 info2 = info->rate.info2;
+       u8 preamble = 0;
+
+       /* Check if valid fields */
+       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+               return;
+
+       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+
+       switch (preamble) {
+       case HTT_RX_LEGACY:
+               cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
+               rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+               rate_idx = 0;
+
+               if (rate < 0x08 || rate > 0x0F)
+                       break;
+
+               switch (band) {
+               case IEEE80211_BAND_2GHZ:
+                       if (cck)
+                               rate &= ~BIT(3);
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       break;
+               case IEEE80211_BAND_5GHZ:
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       /* We are using same rate table registering
+                          HW - ath10k_rates[]. In case of 5GHz skip
+                          CCK rates, so -4 here */
+                       rate_idx -= 4;
+                       break;
+               default:
+                       break;
+               }
+
+               status->rate_idx = rate_idx;
+               break;
+       case HTT_RX_HT:
+       case HTT_RX_HT_WITH_TXBF:
+               /* HT-SIG - Table 20-11 in info1 and info2 */
+               mcs = info1 & 0x1F;
+               nss = mcs >> 3;
+               bw = (info1 >> 7) & 1;
+               sgi = (info2 >> 7) & 1;
+
+               status->rate_idx = mcs;
+               status->flag |= RX_FLAG_HT;
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+               if (bw)
+                       status->flag |= RX_FLAG_40MHZ;
+               break;
+       case HTT_RX_VHT:
+       case HTT_RX_VHT_WITH_TXBF:
+               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+                  TODO check this */
+               mcs = (info2 >> 4) & 0x0F;
+               nss = (info1 >> 10) & 0x07;
+               bw = info1 & 3;
+               sgi = info2 & 1;
+
+               status->rate_idx = mcs;
+               status->vht_nss = nss;
+
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+
+               switch (bw) {
+               /* 20MHZ */
+               case 0:
+                       break;
+               /* 40MHZ */
+               case 1:
+                       status->flag |= RX_FLAG_40MHZ;
+                       break;
+               /* 80MHZ */
+               case 2:
+                       status->flag |= RX_FLAG_80MHZ;
+               }
+
+               status->flag |= RX_FLAG_VHT;
+               break;
+       default:
+               break;
+       }
+}
+
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
+{
+       struct ieee80211_rx_status *status;
+       struct ieee80211_channel *ch;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data;
+
+       status = IEEE80211_SKB_RXCB(info->skb);
+       memset(status, 0, sizeof(*status));
+
+       if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(
+                               __le16_to_cpu(hdr->frame_control) &
+                               ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       if (info->fcs_err)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       status->signal = info->signal;
+
+       spin_lock_bh(&ar->data_lock);
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!ch) {
+               ath10k_warn("no channel configured; ignoring frame!\n");
+               dev_kfree_skb_any(info->skb);
+               return;
+       }
+
+       process_rx_rates(ar, info, ch->band, status);
+       status->band = ch->band;
+       status->freq = ch->center_freq;
+
+       ath10k_dbg(ATH10K_DBG_DATA,
+                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+                  info->skb,
+                  info->skb->len,
+                  status->flag == 0 ? "legacy" : "",
+                  status->flag & RX_FLAG_HT ? "ht" : "",
+                  status->flag & RX_FLAG_VHT ? "vht" : "",
+                  status->flag & RX_FLAG_40MHZ ? "40" : "",
+                  status->flag & RX_FLAG_80MHZ ? "80" : "",
+                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
+                  status->rate_idx,
+                  status->vht_nss,
+                  status->freq,
+                  status->band);
+
+       ieee80211_rx(ar->hw, info->skb);
+}
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+               if (memcmp(peer->addr, addr, ETH_ALEN))
+                       continue;
+
+               return peer;
+       }
+
+       return NULL;
+}
+
+static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar,
+                                                 int peer_id)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list)
+               if (test_bit(peer_id, peer->peer_ids))
+                       return peer;
+
+       return NULL;
+}
+
+static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
+                                      const u8 *addr, bool expect_mapped)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->peer_mapping_wq, ({
+                       bool mapped;
+
+                       spin_lock_bh(&ar->data_lock);
+                       mapped = !!ath10k_peer_find(ar, vdev_id, addr);
+                       spin_unlock_bh(&ar->data_lock);
+
+                       mapped == expect_mapped;
+               }), 3*HZ);
+
+       if (ret <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, true);
+}
+
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, false);
+}
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
+       if (!peer) {
+               peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
+               if (!peer)
+                       goto exit;
+
+               peer->vdev_id = ev->vdev_id;
+               memcpy(peer->addr, ev->addr, ETH_ALEN);
+               list_add(&peer->list, &ar->peers);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
+                  ev->vdev_id, ev->addr, ev->peer_id);
+
+       set_bit(ev->peer_id, peer->peer_ids);
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find_by_id(ar, ev->peer_id);
+       if (!peer) {
+               ath10k_warn("unknown peer id %d\n", ev->peer_id);
+               goto exit;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
+                  peer->vdev_id, peer->addr, ev->peer_id);
+
+       clear_bit(ev->peer_id, peer->peer_ids);
+
+       if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
+               list_del(&peer->list);
+               kfree(peer);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h
new file mode 100644 (file)
index 0000000..e78632a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _TXRX_H_
+#define _TXRX_H_
+
+#include "htt.h"
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done);
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr);
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev);
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
new file mode 100644 (file)
index 0000000..7d4b798
--- /dev/null
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/skbuff.h>
+
+#include "core.h"
+#include "htc.h"
+#include "debug.h"
+#include "wmi.h"
+#include "mac.h"
+
+void ath10k_wmi_flush_tx(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->wmi.wq,
+                                atomic_read(&ar->wmi.pending_tx_count) == 0,
+                                5*HZ);
+       if (atomic_read(&ar->wmi.pending_tx_count) == 0)
+               return;
+
+       if (ret == 0)
+               ret = -ETIMEDOUT;
+
+       if (ret < 0)
+               ath10k_warn("wmi flush failed (%d)\n", ret);
+}
+
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.service_ready,
+                                         WMI_SERVICE_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.unified_ready,
+                                         WMI_UNIFIED_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
+{
+       struct sk_buff *skb;
+       u32 round_len = roundup(len, 4);
+
+       skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len);
+       if (!skb)
+               return NULL;
+
+       skb_reserve(skb, WMI_SKB_HEADROOM);
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned WMI skb\n");
+
+       skb_put(skb, round_len);
+       memset(skb->data, 0, round_len);
+
+       return skb;
+}
+
+static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       dev_kfree_skb(skb);
+
+       if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
+               wake_up(&ar->wmi.wq);
+}
+
+/* WMI command API */
+static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+                              enum wmi_cmd_id cmd_id)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct wmi_cmd_hdr *cmd_hdr;
+       int status;
+       u32 cmd = 0;
+
+       if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return -ENOMEM;
+
+       cmd |= SM(cmd_id, WMI_CMD_HDR_CMD_ID);
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       cmd_hdr->cmd_id = __cpu_to_le32(cmd);
+
+       if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
+           WMI_MAX_PENDING_TX_COUNT) {
+               /* avoid using up memory when FW hangs */
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return -EBUSY;
+       }
+
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
+
+       status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+       if (status) {
+               dev_kfree_skb_any(skb);
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return status;
+       }
+
+       return 0;
+}
+
+static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
+       enum wmi_scan_event_type event_type;
+       enum wmi_scan_completion_reason reason;
+       u32 freq;
+       u32 req_id;
+       u32 scan_id;
+       u32 vdev_id;
+
+       event_type = __le32_to_cpu(event->event_type);
+       reason     = __le32_to_cpu(event->reason);
+       freq       = __le32_to_cpu(event->channel_freq);
+       req_id     = __le32_to_cpu(event->scan_req_id);
+       scan_id    = __le32_to_cpu(event->scan_id);
+       vdev_id    = __le32_to_cpu(event->vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n");
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "scan event type %d reason %d freq %d req_id %d "
+                  "scan_id %d vdev_id %d\n",
+                  event_type, reason, freq, req_id, scan_id, vdev_id);
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (event_type) {
+       case WMI_SCAN_EVENT_STARTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n");
+               if (ar->scan.in_progress && ar->scan.is_roc)
+                       ieee80211_ready_on_channel(ar->hw);
+
+               complete(&ar->scan.started);
+               break;
+       case WMI_SCAN_EVENT_COMPLETED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n");
+               switch (reason) {
+               case WMI_SCAN_REASON_COMPLETED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n");
+                       break;
+               case WMI_SCAN_REASON_CANCELLED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n");
+                       break;
+               case WMI_SCAN_REASON_PREEMPTED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n");
+                       break;
+               case WMI_SCAN_REASON_TIMEDOUT:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n");
+                       break;
+               default:
+                       break;
+               }
+
+               ar->scan_channel = NULL;
+               if (!ar->scan.in_progress) {
+                       ath10k_warn("no scan requested, ignoring\n");
+                       break;
+               }
+
+               if (ar->scan.is_roc) {
+                       ath10k_offchan_tx_purge(ar);
+
+                       if (!ar->scan.aborting)
+                               ieee80211_remain_on_channel_expired(ar->hw);
+               } else {
+                       ieee80211_scan_completed(ar->hw, ar->scan.aborting);
+               }
+
+               del_timer(&ar->scan.timeout);
+               complete_all(&ar->scan.completed);
+               ar->scan.in_progress = false;
+               break;
+       case WMI_SCAN_EVENT_BSS_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n");
+               ar->scan_channel = NULL;
+               break;
+       case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n");
+               ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
+               if (ar->scan.in_progress && ar->scan.is_roc &&
+                   ar->scan.roc_freq == freq) {
+                       complete(&ar->scan.on_channel);
+               }
+               break;
+       case WMI_SCAN_EVENT_DEQUEUED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n");
+               break;
+       case WMI_SCAN_EVENT_PREEMPTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n");
+               break;
+       case WMI_SCAN_EVENT_START_FAILED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n");
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_bh(&ar->data_lock);
+       return 0;
+}
+
+static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode)
+{
+       enum ieee80211_band band;
+
+       switch (phy_mode) {
+       case MODE_11A:
+       case MODE_11NA_HT20:
+       case MODE_11NA_HT40:
+       case MODE_11AC_VHT20:
+       case MODE_11AC_VHT40:
+       case MODE_11AC_VHT80:
+               band = IEEE80211_BAND_5GHZ;
+               break;
+       case MODE_11G:
+       case MODE_11B:
+       case MODE_11GONLY:
+       case MODE_11NG_HT20:
+       case MODE_11NG_HT40:
+       case MODE_11AC_VHT20_2G:
+       case MODE_11AC_VHT40_2G:
+       case MODE_11AC_VHT80_2G:
+       default:
+               band = IEEE80211_BAND_2GHZ;
+       }
+
+       return band;
+}
+
+static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
+{
+       u8 rate_idx = 0;
+
+       /* rate in Kbps */
+       switch (rate) {
+       case 1000:
+               rate_idx = 0;
+               break;
+       case 2000:
+               rate_idx = 1;
+               break;
+       case 5500:
+               rate_idx = 2;
+               break;
+       case 11000:
+               rate_idx = 3;
+               break;
+       case 6000:
+               rate_idx = 4;
+               break;
+       case 9000:
+               rate_idx = 5;
+               break;
+       case 12000:
+               rate_idx = 6;
+               break;
+       case 18000:
+               rate_idx = 7;
+               break;
+       case 24000:
+               rate_idx = 8;
+               break;
+       case 36000:
+               rate_idx = 9;
+               break;
+       case 48000:
+               rate_idx = 10;
+               break;
+       case 54000:
+               rate_idx = 11;
+               break;
+       default:
+               break;
+       }
+
+       if (band == IEEE80211_BAND_5GHZ) {
+               if (rate_idx > 3)
+                       /* Omit CCK rates */
+                       rate_idx -= 4;
+               else
+                       rate_idx = 0;
+       }
+
+       return rate_idx;
+}
+
+static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *hdr;
+       u32 rx_status;
+       u32 channel;
+       u32 phy_mode;
+       u32 snr;
+       u32 rate;
+       u32 buf_len;
+       u16 fc;
+
+       channel   = __le32_to_cpu(event->hdr.channel);
+       buf_len   = __le32_to_cpu(event->hdr.buf_len);
+       rx_status = __le32_to_cpu(event->hdr.status);
+       snr       = __le32_to_cpu(event->hdr.snr);
+       phy_mode  = __le32_to_cpu(event->hdr.phy_mode);
+       rate      = __le32_to_cpu(event->hdr.rate);
+
+       memset(status, 0, sizeof(*status));
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx status %08x\n", rx_status);
+
+       if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_CRC)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+       if (rx_status & WMI_RX_STATUS_ERR_MIC)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       status->band = phy_mode_to_band(phy_mode);
+       status->freq = ieee80211_channel_to_frequency(channel, status->band);
+       status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
+       status->rate_idx = get_rate_idx(rate, status->band);
+
+       skb_pull(skb, sizeof(event->hdr));
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       if (fc & IEEE80211_FCTL_PROTECTED) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(fc &
+                                       ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx skb %p len %d ftype %02x stype %02x\n",
+                  skb, skb->len,
+                  fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE);
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx freq %d band %d snr %d, rate_idx %d\n",
+                  status->freq, status->band, status->signal,
+                  status->rate_idx);
+
+       /*
+        * packets from HTC come aligned to 4byte boundaries
+        * because they can originally come in along with a trailer
+        */
+       skb_trim(skb, buf_len);
+
+       ieee80211_rx(ar->hw, skb);
+       return 0;
+}
+
+static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+}
+
+static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
+}
+
+static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_update_stats(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
+
+       ath10k_debug_read_target_stats(ar, ev);
+}
+
+static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       struct wmi_vdev_start_response_event *ev;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
+
+       ev = (struct wmi_vdev_start_response_event *)skb->data;
+
+       if (WARN_ON(__le32_to_cpu(ev->status)))
+               return;
+
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+}
+
+/*
+ * FIXME
+ *
+ * We don't report to mac80211 sleep state of connected
+ * stations. Due to this mac80211 can't fill in TIM IE
+ * correctly.
+ *
+ * I know of no way of getting nullfunc frames that contain
+ * sleep transition from connected stations - these do not
+ * seem to be sent from the target to the host. There also
+ * doesn't seem to be a dedicated event for that. So the
+ * only way left to do this would be to read tim_bitmap
+ * during SWBA.
+ *
+ * We could probably try using tim_bitmap from SWBA to tell
+ * mac80211 which stations are asleep and which are not. The
+ * problem here is calling mac80211 functions so many times
+ * could take too long and make us miss the time to submit
+ * the beacon to the target.
+ *
+ * So as a workaround we try to extend the TIM IE if there
+ * is unicast buffered for stations with aid > 7 and fill it
+ * in ourselves.
+ */
+static void ath10k_wmi_update_tim(struct ath10k *ar,
+                                 struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
+       struct ieee80211_tim_ie *tim;
+       u8 *ies, *ie;
+       u8 ie_len, pvm_len;
+
+       /* if next SWBA has no tim_changed the tim_bitmap is garbage.
+        * we must copy the bitmap upon change and reuse it later */
+       if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
+               int i;
+
+               BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
+                            sizeof(bcn_info->tim_info.tim_bitmap));
+
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
+                       __le32 t = bcn_info->tim_info.tim_bitmap[i / 4];
+                       u32 v = __le32_to_cpu(t);
+                       arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
+               }
+
+               /* FW reports either length 0 or 16
+                * so we calculate this on our own */
+               arvif->u.ap.tim_len = 0;
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++)
+                       if (arvif->u.ap.tim_bitmap[i])
+                               arvif->u.ap.tim_len = i;
+
+               arvif->u.ap.tim_len++;
+       }
+
+       ies = bcn->data;
+       ies += ieee80211_hdrlen(hdr->frame_control);
+       ies += 12; /* fixed parameters */
+
+       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");
+               return;
+       }
+
+       tim = (void *)ie + 2;
+       ie_len = ie[1];
+       pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */
+
+       if (pvm_len < arvif->u.ap.tim_len) {
+               int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len;
+               int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len);
+               void *next_ie = ie + 2 + ie_len;
+
+               if (skb_put(bcn, expand_size)) {
+                       memmove(next_ie + expand_size, next_ie, move_size);
+
+                       ie[1] += expand_size;
+                       ie_len += expand_size;
+                       pvm_len += expand_size;
+               } else {
+                       ath10k_warn("tim expansion failed\n");
+               }
+       }
+
+       if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) {
+               ath10k_warn("tim pvm length is too great (%d)\n", pvm_len);
+               return;
+       }
+
+       tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
+       memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
+                  tim->dtim_count, tim->dtim_period,
+                  tim->bitmap_ctrl, pvm_len);
+}
+
+static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
+                                  struct wmi_p2p_noa_info *noa)
+{
+       struct ieee80211_p2p_noa_attr *noa_attr;
+       u8  ctwindow_oppps = noa->ctwindow_oppps;
+       u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET;
+       bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT);
+       __le16 *noa_attr_len;
+       u16 attr_len;
+       u8 noa_descriptors = noa->num_descriptors;
+       int i;
+
+       /* P2P IE */
+       data[0] = WLAN_EID_VENDOR_SPECIFIC;
+       data[1] = len - 2;
+       data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
+       data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
+       data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
+       data[5] = WLAN_OUI_TYPE_WFA_P2P;
+
+       /* NOA ATTR */
+       data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
+       noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
+       noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
+
+       noa_attr->index = noa->index;
+       noa_attr->oppps_ctwindow = ctwindow;
+       if (oppps)
+               noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
+
+       for (i = 0; i < noa_descriptors; i++) {
+               noa_attr->desc[i].count =
+                       __le32_to_cpu(noa->descriptors[i].type_count);
+               noa_attr->desc[i].duration = noa->descriptors[i].duration;
+               noa_attr->desc[i].interval = noa->descriptors[i].interval;
+               noa_attr->desc[i].start_time = noa->descriptors[i].start_time;
+       }
+
+       attr_len = 2; /* index + oppps_ctwindow */
+       attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+       *noa_attr_len = __cpu_to_le16(attr_len);
+}
+
+static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
+{
+       u32 len = 0;
+       u8 noa_descriptors = noa->num_descriptors;
+       u8 opp_ps_info = noa->ctwindow_oppps;
+       bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT);
+
+
+       if (!noa_descriptors && !opps_enabled)
+               return len;
+
+       len += 1 + 1 + 4; /* EID + len + OUI */
+       len += 1 + 2; /* noa attr  + attr len */
+       len += 1 + 1; /* index + oppps_ctwindow */
+       len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+
+       return len;
+}
+
+static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
+       u8 *new_data, *old_data = arvif->u.ap.noa_data;
+       u32 new_len;
+
+       if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
+       if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) {
+               new_len = ath10k_p2p_calc_noa_ie_len(noa);
+               if (!new_len)
+                       goto cleanup;
+
+               new_data = kmalloc(new_len, GFP_ATOMIC);
+               if (!new_data)
+                       goto cleanup;
+
+               ath10k_p2p_fill_noa_ie(new_data, new_len, noa);
+
+               spin_lock_bh(&ar->data_lock);
+               arvif->u.ap.noa_data = new_data;
+               arvif->u.ap.noa_len = new_len;
+               spin_unlock_bh(&ar->data_lock);
+               kfree(old_data);
+       }
+
+       if (arvif->u.ap.noa_data)
+               if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC))
+                       memcpy(skb_put(bcn, arvif->u.ap.noa_len),
+                              arvif->u.ap.noa_data,
+                              arvif->u.ap.noa_len);
+       return;
+
+cleanup:
+       spin_lock_bh(&ar->data_lock);
+       arvif->u.ap.noa_data = NULL;
+       arvif->u.ap.noa_len = 0;
+       spin_unlock_bh(&ar->data_lock);
+       kfree(old_data);
+}
+
+
+static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_host_swba_event *ev;
+       u32 map;
+       int i = -1;
+       struct wmi_bcn_info *bcn_info;
+       struct ath10k_vif *arvif;
+       struct wmi_bcn_tx_arg arg;
+       struct sk_buff *bcn;
+       int vdev_id = 0;
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
+
+       ev = (struct wmi_host_swba_event *)skb->data;
+       map = __le32_to_cpu(ev->vdev_map);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n"
+                  "-vdev map 0x%x\n",
+                  ev->vdev_map);
+
+       for (; map; map >>= 1, vdev_id++) {
+               if (!(map & 0x1))
+                       continue;
+
+               i++;
+
+               if (i >= WMI_MAX_AP_VDEV) {
+                       ath10k_warn("swba has corrupted vdev map\n");
+                       break;
+               }
+
+               bcn_info = &ev->bcn_info[i];
+
+               ath10k_dbg(ATH10K_DBG_MGMT,
+                          "-bcn_info[%d]:\n"
+                          "--tim_len %d\n"
+                          "--tim_mcast %d\n"
+                          "--tim_changed %d\n"
+                          "--tim_num_ps_pending %d\n"
+                          "--tim_bitmap 0x%08x%08x%08x%08x\n",
+                          i,
+                          __le32_to_cpu(bcn_info->tim_info.tim_len),
+                          __le32_to_cpu(bcn_info->tim_info.tim_mcast),
+                          __le32_to_cpu(bcn_info->tim_info.tim_changed),
+                          __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
+
+               arvif = ath10k_get_arvif(ar, vdev_id);
+               if (arvif == NULL) {
+                       ath10k_warn("no vif for vdev_id %d found\n", vdev_id);
+                       continue;
+               }
+
+               bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
+               if (!bcn) {
+                       ath10k_warn("could not get mac80211 beacon\n");
+                       continue;
+               }
+
+               ath10k_tx_h_seq_no(bcn);
+               ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
+               ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
+
+               arg.vdev_id = arvif->vdev_id;
+               arg.tx_rate = 0;
+               arg.tx_power = 0;
+               arg.bcn = bcn->data;
+               arg.bcn_len = bcn->len;
+
+               ret = ath10k_wmi_beacon_send(ar, &arg);
+               if (ret)
+                       ath10k_warn("could not send beacon (%d)\n", ret);
+
+               dev_kfree_skb_any(bcn);
+       }
+}
+
+static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+}
+
+static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
+}
+
+static void ath10k_wmi_event_profile_match(struct ath10k *ar,
+                                   struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
+}
+
+static void ath10k_wmi_event_debug_print(struct ath10k *ar,
+                                 struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
+}
+
+static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
+                                        struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
+}
+
+static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+                                               struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       struct wmi_service_ready_event *ev = (void *)skb->data;
+
+       if (skb->len < sizeof(*ev)) {
+               ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+                           skb->len, sizeof(*ev));
+               return;
+       }
+
+       ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+       ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+       ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+       ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+       ar->fw_version_major =
+               (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+       ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+       ar->fw_version_release =
+               (__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->ath_common.regulatory.current_rd =
+               __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+       ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+                                     sizeof(ev->wmi_service_bitmap));
+
+       if (strlen(ar->hw->wiphy->fw_version) == 0) {
+               snprintf(ar->hw->wiphy->fw_version,
+                        sizeof(ar->hw->wiphy->fw_version),
+                        "%u.%u.%u.%u",
+                        ar->fw_version_major,
+                        ar->fw_version_minor,
+                        ar->fw_version_release,
+                        ar->fw_version_build);
+       }
+
+       /* FIXME: it probably should be better to support this */
+       if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
+               ath10k_warn("target requested %d memory chunks; ignoring\n",
+                           __le32_to_cpu(ev->num_mem_reqs));
+       }
+
+       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",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->sw_version_1),
+                  __le32_to_cpu(ev->abi_version),
+                  __le32_to_cpu(ev->phy_capability),
+                  __le32_to_cpu(ev->ht_cap_info),
+                  __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));
+
+       complete(&ar->wmi.service_ready);
+}
+
+static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
+
+       if (WARN_ON(skb->len < sizeof(*ev)))
+               return -EINVAL;
+
+       memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->abi_version),
+                  ev->mac_addr.addr,
+                  __le32_to_cpu(ev->status));
+
+       complete(&ar->wmi.unified_ready);
+       return 0;
+}
+
+static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr;
+       enum wmi_event_id id;
+       u16 len;
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return;
+
+       len = skb->len;
+
+       trace_ath10k_wmi_event(id, skb->data, skb->len);
+
+       switch (id) {
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_mgmt_rx(ar, skb);
+               /* mgmt_rx() owns the skb now! */
+               return;
+       case WMI_SCAN_EVENTID:
+               ath10k_wmi_event_scan(ar, skb);
+               break;
+       case WMI_CHAN_INFO_EVENTID:
+               ath10k_wmi_event_chan_info(ar, skb);
+               break;
+       case WMI_ECHO_EVENTID:
+               ath10k_wmi_event_echo(ar, skb);
+               break;
+       case WMI_DEBUG_MESG_EVENTID:
+               ath10k_wmi_event_debug_mesg(ar, skb);
+               break;
+       case WMI_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
+       case WMI_VDEV_START_RESP_EVENTID:
+               ath10k_wmi_event_vdev_start_resp(ar, skb);
+               break;
+       case WMI_VDEV_STOPPED_EVENTID:
+               ath10k_wmi_event_vdev_stopped(ar, skb);
+               break;
+       case WMI_PEER_STA_KICKOUT_EVENTID:
+               ath10k_wmi_event_peer_sta_kickout(ar, skb);
+               break;
+       case WMI_HOST_SWBA_EVENTID:
+               ath10k_wmi_event_host_swba(ar, skb);
+               break;
+       case WMI_TBTTOFFSET_UPDATE_EVENTID:
+               ath10k_wmi_event_tbttoffset_update(ar, skb);
+               break;
+       case WMI_PHYERR_EVENTID:
+               ath10k_wmi_event_phyerr(ar, skb);
+               break;
+       case WMI_ROAM_EVENTID:
+               ath10k_wmi_event_roam(ar, skb);
+               break;
+       case WMI_PROFILE_MATCH:
+               ath10k_wmi_event_profile_match(ar, skb);
+               break;
+       case WMI_DEBUG_PRINT_EVENTID:
+               ath10k_wmi_event_debug_print(ar, skb);
+               break;
+       case WMI_PDEV_QVIT_EVENTID:
+               ath10k_wmi_event_pdev_qvit(ar, skb);
+               break;
+       case WMI_WLAN_PROFILE_DATA_EVENTID:
+               ath10k_wmi_event_wlan_profile_data(ar, skb);
+               break;
+       case WMI_RTT_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_measurement_report(ar, skb);
+               break;
+       case WMI_TSF_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_tsf_measurement_report(ar, skb);
+               break;
+       case WMI_RTT_ERROR_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_error_report(ar, skb);
+               break;
+       case WMI_WOW_WAKEUP_HOST_EVENTID:
+               ath10k_wmi_event_wow_wakeup_host(ar, skb);
+               break;
+       case WMI_DCS_INTERFERENCE_EVENTID:
+               ath10k_wmi_event_dcs_interference(ar, skb);
+               break;
+       case WMI_PDEV_TPC_CONFIG_EVENTID:
+               ath10k_wmi_event_pdev_tpc_config(ar, skb);
+               break;
+       case WMI_PDEV_FTM_INTG_EVENTID:
+               ath10k_wmi_event_pdev_ftm_intg(ar, skb);
+               break;
+       case WMI_GTK_OFFLOAD_STATUS_EVENTID:
+               ath10k_wmi_event_gtk_offload_status(ar, skb);
+               break;
+       case WMI_GTK_REKEY_FAIL_EVENTID:
+               ath10k_wmi_event_gtk_rekey_fail(ar, skb);
+               break;
+       case WMI_TX_DELBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_delba_complete(ar, skb);
+               break;
+       case WMI_TX_ADDBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_addba_complete(ar, skb);
+               break;
+       case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID:
+               ath10k_wmi_event_vdev_install_key_complete(ar, skb);
+               break;
+       case WMI_SERVICE_READY_EVENTID:
+               ath10k_wmi_service_ready_event_rx(ar, skb);
+               break;
+       case WMI_READY_EVENTID:
+               ath10k_wmi_ready_event_rx(ar, skb);
+               break;
+       default:
+               ath10k_warn("Unknown eventid: %d\n", id);
+               break;
+       }
+
+       dev_kfree_skb(skb);
+}
+
+static void ath10k_wmi_event_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k,
+                                        wmi.wmi_event_work);
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi.wmi_event_list);
+               if (!skb)
+                       break;
+
+               ath10k_wmi_event_process(ar, skb);
+       }
+}
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       enum wmi_event_id event_id;
+
+       event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       /* some events require to be handled ASAP
+        * thus can't be defered to a worker thread */
+       switch (event_id) {
+       case WMI_HOST_SWBA_EVENTID:
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_process(ar, skb);
+               return;
+       default:
+               break;
+       }
+
+       skb_queue_tail(&ar->wmi.wmi_event_list, skb);
+       queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
+}
+
+/* WMI Initialization functions */
+int ath10k_wmi_attach(struct ath10k *ar)
+{
+       init_completion(&ar->wmi.service_ready);
+       init_completion(&ar->wmi.unified_ready);
+       init_waitqueue_head(&ar->wmi.wq);
+
+       skb_queue_head_init(&ar->wmi.wmi_event_list);
+       INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
+
+       return 0;
+}
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+       /* HTC should've drained the packets already */
+       if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
+               ath10k_warn("there are still pending packets\n");
+
+       cancel_work_sync(&ar->wmi.wmi_event_work);
+       skb_queue_purge(&ar->wmi.wmi_event_list);
+}
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar)
+{
+       int status;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       /* these fields are the same for all service endpoints */
+       conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
+
+       /* 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);
+       if (status) {
+               ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
+                           status);
+               return status;
+       }
+
+       ar->wmi.eid = conn_resp.eid;
+       return 0;
+}
+
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g)
+{
+       struct wmi_pdev_set_regdomain_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
+       cmd->reg_domain = __cpu_to_le32(rd);
+       cmd->reg_domain_2G = __cpu_to_le32(rd2g);
+       cmd->reg_domain_5G = __cpu_to_le32(rd5g);
+       cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g);
+       cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
+                  rd, rd2g, rd5g, ctl2g, ctl5g);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+}
+
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *arg)
+{
+       struct wmi_set_channel_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->passive)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_set_channel_cmd *)skb->data;
+       cmd->chan.mhz               = __cpu_to_le32(arg->freq);
+       cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
+       cmd->chan.mode              = arg->mode;
+       cmd->chan.min_power         = arg->min_power;
+       cmd->chan.max_power         = arg->max_power;
+       cmd->chan.reg_power         = arg->max_reg_power;
+       cmd->chan.reg_classid       = arg->reg_class_id;
+       cmd->chan.antenna_max       = arg->max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set channel mode %d freq %d\n",
+                  arg->mode, arg->freq);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+}
+
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+{
+       struct wmi_pdev_suspend_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
+       cmd->suspend_opt = WMI_PDEV_SUSPEND;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+}
+
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(0);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+}
+
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value)
+{
+       struct wmi_pdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
+       cmd->param_id    = __cpu_to_le32(id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
+                  id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+       struct wmi_init_cmd *cmd;
+       struct sk_buff *buf;
+       struct wmi_resource_config config = {};
+       u32 val;
+
+       config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+       config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
+
+       config.num_offload_reorder_bufs =
+               __cpu_to_le32(TARGET_NUM_OFFLOAD_REORDER_BUFS);
+
+       config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS);
+       config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS);
+       config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT);
+       config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK);
+       config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK);
+       config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI);
+       config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE);
+
+       config.scan_max_pending_reqs =
+               __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS);
+
+       config.bmiss_offload_max_vdev =
+               __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_vdev =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_ap_profiles =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+       config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS);
+       config.num_mcast_table_elems =
+               __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS);
+
+       config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE);
+       config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE);
+       config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES);
+       config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE);
+       config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM);
+
+       val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+       config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+       config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG);
+
+       config.gtk_offload_max_vdev =
+               __cpu_to_le32(TARGET_GTK_OFFLOAD_MAX_VDEV);
+
+       config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
+       config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
+
+       buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!buf)
+               return -ENOMEM;
+
+       cmd = (struct wmi_init_cmd *)buf->data;
+       cmd->num_host_mem_chunks = 0;
+       memcpy(&cmd->resource_config, &config, sizeof(config));
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
+       return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+}
+
+static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+{
+       int len;
+
+       len = sizeof(struct wmi_start_scan_cmd);
+
+       if (arg->ie_len) {
+               if (!arg->ie)
+                       return -EINVAL;
+               if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ie_data);
+               len += roundup(arg->ie_len, 4);
+       }
+
+       if (arg->n_channels) {
+               if (!arg->channels)
+                       return -EINVAL;
+               if (arg->n_channels > ARRAY_SIZE(arg->channels))
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_chan_list);
+               len += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               if (!arg->ssids)
+                       return -EINVAL;
+               if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ssid_list);
+               len += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               if (!arg->bssids)
+                       return -EINVAL;
+               if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_bssid_list);
+               len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       return len;
+}
+
+int ath10k_wmi_start_scan(struct ath10k *ar,
+                         const struct wmi_start_scan_arg *arg)
+{
+       struct wmi_start_scan_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_ie_data *ie;
+       struct wmi_chan_list *channels;
+       struct wmi_ssid_list *ssids;
+       struct wmi_bssid_list *bssids;
+       u32 scan_id;
+       u32 scan_req_id;
+       int off;
+       int len = 0;
+       int i;
+
+       len = ath10k_wmi_start_scan_calc_len(arg);
+       if (len < 0)
+               return len; /* len contains error code here */
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id  = WMI_HOST_SCAN_REQ_ID_PREFIX;
+       scan_id |= arg->scan_id;
+
+       scan_req_id  = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+       scan_req_id |= arg->scan_req_id;
+
+       cmd = (struct wmi_start_scan_cmd *)skb->data;
+       cmd->scan_id            = __cpu_to_le32(scan_id);
+       cmd->scan_req_id        = __cpu_to_le32(scan_req_id);
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->scan_priority      = __cpu_to_le32(arg->scan_priority);
+       cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+       cmd->dwell_time_active  = __cpu_to_le32(arg->dwell_time_active);
+       cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+       cmd->min_rest_time      = __cpu_to_le32(arg->min_rest_time);
+       cmd->max_rest_time      = __cpu_to_le32(arg->max_rest_time);
+       cmd->repeat_probe_time  = __cpu_to_le32(arg->repeat_probe_time);
+       cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+       cmd->idle_time          = __cpu_to_le32(arg->idle_time);
+       cmd->max_scan_time      = __cpu_to_le32(arg->max_scan_time);
+       cmd->probe_delay        = __cpu_to_le32(arg->probe_delay);
+       cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
+
+       /* TLV list starts after fields included in the struct */
+       off = sizeof(*cmd);
+
+       if (arg->n_channels) {
+               channels = (void *)skb->data + off;
+               channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
+               channels->num_chan = __cpu_to_le32(arg->n_channels);
+
+               for (i = 0; i < arg->n_channels; i++)
+                       channels->channel_list[i] =
+                               __cpu_to_le32(arg->channels[i]);
+
+               off += sizeof(*channels);
+               off += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               ssids = (void *)skb->data + off;
+               ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
+               ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
+
+               for (i = 0; i < arg->n_ssids; i++) {
+                       ssids->ssids[i].ssid_len =
+                               __cpu_to_le32(arg->ssids[i].len);
+                       memcpy(&ssids->ssids[i].ssid,
+                              arg->ssids[i].ssid,
+                              arg->ssids[i].len);
+               }
+
+               off += sizeof(*ssids);
+               off += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               bssids = (void *)skb->data + off;
+               bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
+               bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
+
+               for (i = 0; i < arg->n_bssids; i++)
+                       memcpy(&bssids->bssid_list[i],
+                              arg->bssids[i].bssid,
+                              ETH_ALEN);
+
+               off += sizeof(*bssids);
+               off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       if (arg->ie_len) {
+               ie = (void *)skb->data + off;
+               ie->tag = __cpu_to_le32(WMI_IE_TAG);
+               ie->ie_len = __cpu_to_le32(arg->ie_len);
+               memcpy(ie->ie_data, arg->ie, arg->ie_len);
+
+               off += sizeof(*ie);
+               off += roundup(arg->ie_len, 4);
+       }
+
+       if (off != skb->len) {
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+}
+
+void ath10k_wmi_start_scan_init(struct ath10k *ar,
+                               struct wmi_start_scan_arg *arg)
+{
+       /* setup commonly used values */
+       arg->scan_req_id = 1;
+       arg->scan_priority = WMI_SCAN_PRIORITY_LOW;
+       arg->dwell_time_active = 50;
+       arg->dwell_time_passive = 150;
+       arg->min_rest_time = 50;
+       arg->max_rest_time = 500;
+       arg->repeat_probe_time = 0;
+       arg->probe_spacing_time = 0;
+       arg->idle_time = 0;
+       arg->max_scan_time = 5000;
+       arg->probe_delay = 5;
+       arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
+               | WMI_SCAN_EVENT_COMPLETED
+               | WMI_SCAN_EVENT_BSS_CHANNEL
+               | WMI_SCAN_EVENT_FOREIGN_CHANNEL
+               | WMI_SCAN_EVENT_DEQUEUED;
+       arg->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES;
+       arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT;
+       arg->n_bssids = 1;
+       arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
+}
+
+int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
+{
+       struct wmi_stop_scan_cmd *cmd;
+       struct sk_buff *skb;
+       u32 scan_id;
+       u32 req_id;
+
+       if (arg->req_id > 0xFFF)
+               return -EINVAL;
+       if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id = arg->u.scan_id;
+       scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
+
+       req_id = arg->req_id;
+       req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+
+       cmd = (struct wmi_stop_scan_cmd *)skb->data;
+       cmd->req_type    = __cpu_to_le32(arg->req_type);
+       cmd->vdev_id     = __cpu_to_le32(arg->u.vdev_id);
+       cmd->scan_id     = __cpu_to_le32(scan_id);
+       cmd->scan_req_id = __cpu_to_le32(req_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
+                  arg->req_id, arg->req_type, arg->u.scan_id);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+}
+
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN])
+{
+       struct wmi_vdev_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_create_cmd *)skb->data;
+       cmd->vdev_id      = __cpu_to_le32(vdev_id);
+       cmd->vdev_type    = __cpu_to_le32(type);
+       cmd->vdev_subtype = __cpu_to_le32(subtype);
+       memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
+                  vdev_id, type, subtype, macaddr);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+}
+
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev delete id %d\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+}
+
+static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
+                               const struct wmi_vdev_start_request_arg *arg,
+                               enum wmi_cmd_id cmd_id)
+{
+       struct wmi_vdev_start_request_cmd *cmd;
+       struct sk_buff *skb;
+       const char *cmdname;
+       u32 flags = 0;
+
+       if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
+           cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+               return -EINVAL;
+       if (WARN_ON(arg->ssid && arg->ssid_len == 0))
+               return -EINVAL;
+       if (WARN_ON(arg->hidden_ssid && !arg->ssid))
+               return -EINVAL;
+       if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
+               return -EINVAL;
+
+       if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+               cmdname = "start";
+       else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+               cmdname = "restart";
+       else
+               return -EINVAL; /* should not happen, we already check cmd_id */
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       if (arg->hidden_ssid)
+               flags |= WMI_VDEV_START_HIDDEN_SSID;
+       if (arg->pmf_enabled)
+               flags |= WMI_VDEV_START_PMF_ENABLED;
+
+       cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
+       cmd->disable_hw_ack  = __cpu_to_le32(arg->disable_hw_ack);
+       cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval);
+       cmd->dtim_period     = __cpu_to_le32(arg->dtim_period);
+       cmd->flags           = __cpu_to_le32(flags);
+       cmd->bcn_tx_rate     = __cpu_to_le32(arg->bcn_tx_rate);
+       cmd->bcn_tx_power    = __cpu_to_le32(arg->bcn_tx_power);
+
+       if (arg->ssid) {
+               cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len);
+               memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
+       }
+
+       cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
+
+       cmd->chan.band_center_freq1 =
+               __cpu_to_le32(arg->channel.band_center_freq1);
+
+       cmd->chan.mode = arg->channel.mode;
+       cmd->chan.min_power = arg->channel.min_power;
+       cmd->chan.max_power = arg->channel.max_power;
+       cmd->chan.reg_power = arg->channel.max_reg_power;
+       cmd->chan.reg_classid = arg->channel.reg_class_id;
+       cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
+                  "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
+                  arg->channel.mode, flags, arg->channel.max_power);
+
+       return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_START_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                    const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_RESTART_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_stop_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_stop_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+}
+
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
+{
+       struct wmi_vdev_up_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_up_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(vdev_id);
+       cmd->vdev_assoc_id = __cpu_to_le32(aid);
+       memcpy(&cmd->vdev_bssid.addr, bssid, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
+                  vdev_id, aid, bssid);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+}
+
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_down_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_down_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev down id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+}
+
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value)
+{
+       struct wmi_vdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev id 0x%x set param %d value %d\n",
+                  vdev_id, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg)
+{
+       struct wmi_vdev_install_key_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
+               return -EINVAL;
+       if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(arg->vdev_id);
+       cmd->key_idx       = __cpu_to_le32(arg->key_idx);
+       cmd->key_flags     = __cpu_to_le32(arg->key_flags);
+       cmd->key_cipher    = __cpu_to_le32(arg->key_cipher);
+       cmd->key_len       = __cpu_to_le32(arg->key_len);
+       cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len);
+       cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
+
+       if (arg->macaddr)
+               memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN);
+       if (arg->key_data)
+               memcpy(cmd->key_data, arg->key_data, arg->key_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+}
+
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_create_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer create vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+}
+
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer delete vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+}
+
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                         const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+{
+       struct wmi_peer_flush_tids_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(vdev_id);
+       cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
+                  vdev_id, peer_addr, tid_bitmap);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+}
+
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr, enum wmi_peer_param param_id,
+                             u32 param_value)
+{
+       struct wmi_peer_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+       memcpy(&cmd->peer_macaddr.addr, peer_addr, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %d peer 0x%pM set param %d value %d\n",
+                  vdev_id, peer_addr, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode)
+{
+       struct wmi_sta_powersave_mode_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->sta_ps_mode = __cpu_to_le32(psmode);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set powersave id 0x%x mode %d\n",
+                  vdev_id, psmode);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+}
+
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value)
+{
+       struct wmi_sta_powersave_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi sta ps param vdev_id 0x%x param %d value %d\n",
+                  vdev_id, param_id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value)
+{
+       struct wmi_ap_ps_peer_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (!mac)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       cmd->param_id = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+       memcpy(&cmd->peer_macaddr, mac, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
+                  vdev_id, param_id, value, mac);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+}
+
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg)
+{
+       struct wmi_scan_chan_list_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_channel_arg *ch;
+       struct wmi_channel *ci;
+       int len;
+       int i;
+
+       len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel);
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -EINVAL;
+
+       cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+       cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
+
+       for (i = 0; i < arg->n_channels; i++) {
+               u32 flags = 0;
+
+               ch = &arg->channels[i];
+               ci = &cmd->chan_info[i];
+
+               if (ch->passive)
+                       flags |= WMI_CHAN_FLAG_PASSIVE;
+               if (ch->allow_ibss)
+                       flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+               if (ch->allow_ht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_HT;
+               if (ch->allow_vht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+               if (ch->ht40plus)
+                       flags |= WMI_CHAN_FLAG_HT40_PLUS;
+
+               ci->mhz               = __cpu_to_le32(ch->freq);
+               ci->band_center_freq1 = __cpu_to_le32(ch->freq);
+               ci->band_center_freq2 = 0;
+               ci->min_power         = ch->min_power;
+               ci->max_power         = ch->max_power;
+               ci->reg_power         = ch->max_reg_power;
+               ci->antenna_max       = ch->max_antenna_gain;
+               ci->antenna_max       = 0;
+
+               /* mode & flags share storage */
+               ci->mode              = ch->mode;
+               ci->flags            |= __cpu_to_le32(flags);
+       }
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+}
+
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_peer_assoc_complete_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->peer_mpdu_density > 16)
+               return -EINVAL;
+       if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+       if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data;
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->peer_new_assoc     = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
+       cmd->peer_associd       = __cpu_to_le32(arg->peer_aid);
+       cmd->peer_flags         = __cpu_to_le32(arg->peer_flags);
+       cmd->peer_caps          = __cpu_to_le32(arg->peer_caps);
+       cmd->peer_listen_intval = __cpu_to_le32(arg->peer_listen_intval);
+       cmd->peer_ht_caps       = __cpu_to_le32(arg->peer_ht_caps);
+       cmd->peer_max_mpdu      = __cpu_to_le32(arg->peer_max_mpdu);
+       cmd->peer_mpdu_density  = __cpu_to_le32(arg->peer_mpdu_density);
+       cmd->peer_rate_caps     = __cpu_to_le32(arg->peer_rate_caps);
+       cmd->peer_nss           = __cpu_to_le32(arg->peer_num_spatial_streams);
+       cmd->peer_vht_caps      = __cpu_to_le32(arg->peer_vht_caps);
+       cmd->peer_phymode       = __cpu_to_le32(arg->peer_phymode);
+
+       memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN);
+
+       cmd->peer_legacy_rates.num_rates =
+               __cpu_to_le32(arg->peer_legacy_rates.num_rates);
+       memcpy(cmd->peer_legacy_rates.rates, arg->peer_legacy_rates.rates,
+              arg->peer_legacy_rates.num_rates);
+
+       cmd->peer_ht_rates.num_rates =
+               __cpu_to_le32(arg->peer_ht_rates.num_rates);
+       memcpy(cmd->peer_ht_rates.rates, arg->peer_ht_rates.rates,
+              arg->peer_ht_rates.num_rates);
+
+       cmd->peer_vht_rates.rx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.rx_max_rate);
+       cmd->peer_vht_rates.rx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set);
+       cmd->peer_vht_rates.tx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
+       cmd->peer_vht_rates.tx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+}
+
+int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
+{
+       struct wmi_bcn_tx_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_bcn_tx_cmd *)skb->data;
+       cmd->hdr.vdev_id  = __cpu_to_le32(arg->vdev_id);
+       cmd->hdr.tx_rate  = __cpu_to_le32(arg->tx_rate);
+       cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
+       cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
+       memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
+}
+
+static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
+                                         const struct wmi_wmm_params_arg *arg)
+{
+       params->cwmin  = __cpu_to_le32(arg->cwmin);
+       params->cwmax  = __cpu_to_le32(arg->cwmax);
+       params->aifs   = __cpu_to_le32(arg->aifs);
+       params->txop   = __cpu_to_le32(arg->txop);
+       params->acm    = __cpu_to_le32(arg->acm);
+       params->no_ack = __cpu_to_le32(arg->no_ack);
+}
+
+int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+                       const struct wmi_pdev_set_wmm_params_arg *arg)
+{
+       struct wmi_pdev_set_wmm_params *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+}
+
+int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+{
+       struct wmi_request_stats_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_request_stats_cmd *)skb->data;
+       cmd->stats_id = __cpu_to_le32(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);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
new file mode 100644 (file)
index 0000000..9555f5a
--- /dev/null
@@ -0,0 +1,3052 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WMI_H_
+#define _WMI_H_
+
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+/*
+ * This file specifies the WMI interface for the Unified Software
+ * Architecture.
+ *
+ * It includes definitions of all the commands and events. Commands are
+ * messages from the host to the target. Events and Replies are messages
+ * from the target to the host.
+ *
+ * Ownership of correctness in regards to WMI commands belongs to the host
+ * driver and the target is not required to validate parameters for value,
+ * proper range, or any other checking.
+ *
+ * Guidelines for extending this interface are below.
+ *
+ * 1. Add new WMI commands ONLY within the specified range - 0x9000 - 0x9fff
+ *
+ * 2. Use ONLY u32 type for defining member variables within WMI
+ *    command/event structures. Do not use u8, u16, bool or
+ *    enum types within these structures.
+ *
+ * 3. DO NOT define bit fields within structures. Implement bit fields
+ *    using masks if necessary. Do not use the programming language's bit
+ *    field definition.
+ *
+ * 4. Define macros for encode/decode of u8, u16 fields within
+ *    the u32 variables. Use these macros for set/get of these fields.
+ *    Try to use this to optimize the structure without bloating it with
+ *    u32 variables for every lower sized field.
+ *
+ * 5. Do not use PACK/UNPACK attributes for the structures as each member
+ *    variable is already 4-byte aligned by virtue of being a u32
+ *    type.
+ *
+ * 6. Comment each parameter part of the WMI command/event structure by
+ *    using the 2 stars at the begining of C comment instead of one star to
+ *    enable HTML document generation using Doxygen.
+ *
+ */
+
+/* Control Path */
+struct wmi_cmd_hdr {
+       __le32 cmd_id;
+} __packed;
+
+#define WMI_CMD_HDR_CMD_ID_MASK   0x00FFFFFF
+#define WMI_CMD_HDR_CMD_ID_LSB    0
+#define WMI_CMD_HDR_PLT_PRIV_MASK 0xFF000000
+#define WMI_CMD_HDR_PLT_PRIV_LSB  24
+
+#define HTC_PROTOCOL_VERSION    0x0002
+#define WMI_PROTOCOL_VERSION    0x0002
+
+enum wmi_service_id {
+       WMI_SERVICE_BEACON_OFFLOAD = 0,   /* beacon offload */
+       WMI_SERVICE_SCAN_OFFLOAD,         /* scan offload */
+       WMI_SERVICE_ROAM_OFFLOAD,         /* roam offload */
+       WMI_SERVICE_BCN_MISS_OFFLOAD,     /* beacon miss offload */
+       WMI_SERVICE_STA_PWRSAVE,          /* fake sleep + basic power save */
+       WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */
+       WMI_SERVICE_AP_UAPSD,             /* uapsd on AP */
+       WMI_SERVICE_AP_DFS,               /* DFS on AP */
+       WMI_SERVICE_11AC,                 /* supports 11ac */
+       WMI_SERVICE_BLOCKACK,   /* Supports triggering ADDBA/DELBA from host*/
+       WMI_SERVICE_PHYERR,               /* PHY error */
+       WMI_SERVICE_BCN_FILTER,           /* Beacon filter support */
+       WMI_SERVICE_RTT,                  /* RTT (round trip time) support */
+       WMI_SERVICE_RATECTRL,             /* Rate-control */
+       WMI_SERVICE_WOW,                  /* WOW Support */
+       WMI_SERVICE_RATECTRL_CACHE,       /* Rate-control caching */
+       WMI_SERVICE_IRAM_TIDS,            /* TIDs in IRAM */
+       WMI_SERVICE_ARPNS_OFFLOAD,        /* ARP NS Offload support */
+       WMI_SERVICE_NLO,                  /* Network list offload service */
+       WMI_SERVICE_GTK_OFFLOAD,          /* GTK offload */
+       WMI_SERVICE_SCAN_SCH,             /* Scan Scheduler Service */
+       WMI_SERVICE_CSA_OFFLOAD,          /* CSA offload service */
+       WMI_SERVICE_CHATTER,              /* Chatter service */
+       WMI_SERVICE_COEX_FREQAVOID,       /* FW report freq range to avoid */
+       WMI_SERVICE_PACKET_POWER_SAVE,    /* packet power save service */
+       WMI_SERVICE_FORCE_FW_HANG,        /* To test fw recovery mechanism */
+       WMI_SERVICE_GPIO,                 /* GPIO service */
+       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */
+       WMI_STA_UAPSD_BASIC_AUTO_TRIG,    /* UAPSD AC Trigger Generation  */
+       WMI_STA_UAPSD_VAR_AUTO_TRIG,      /* -do- */
+       WMI_SERVICE_STA_KEEP_ALIVE,       /* STA keep alive mechanism support */
+       WMI_SERVICE_TX_ENCAP,             /* Packet type for TX encapsulation */
+
+       WMI_SERVICE_LAST,
+       WMI_MAX_SERVICE = 64              /* max service */
+};
+
+static inline char *wmi_service_name(int service_id)
+{
+       switch (service_id) {
+       case WMI_SERVICE_BEACON_OFFLOAD:
+               return "BEACON_OFFLOAD";
+       case WMI_SERVICE_SCAN_OFFLOAD:
+               return "SCAN_OFFLOAD";
+       case WMI_SERVICE_ROAM_OFFLOAD:
+               return "ROAM_OFFLOAD";
+       case WMI_SERVICE_BCN_MISS_OFFLOAD:
+               return "BCN_MISS_OFFLOAD";
+       case WMI_SERVICE_STA_PWRSAVE:
+               return "STA_PWRSAVE";
+       case WMI_SERVICE_STA_ADVANCED_PWRSAVE:
+               return "STA_ADVANCED_PWRSAVE";
+       case WMI_SERVICE_AP_UAPSD:
+               return "AP_UAPSD";
+       case WMI_SERVICE_AP_DFS:
+               return "AP_DFS";
+       case WMI_SERVICE_11AC:
+               return "11AC";
+       case WMI_SERVICE_BLOCKACK:
+               return "BLOCKACK";
+       case WMI_SERVICE_PHYERR:
+               return "PHYERR";
+       case WMI_SERVICE_BCN_FILTER:
+               return "BCN_FILTER";
+       case WMI_SERVICE_RTT:
+               return "RTT";
+       case WMI_SERVICE_RATECTRL:
+               return "RATECTRL";
+       case WMI_SERVICE_WOW:
+               return "WOW";
+       case WMI_SERVICE_RATECTRL_CACHE:
+               return "RATECTRL CACHE";
+       case WMI_SERVICE_IRAM_TIDS:
+               return "IRAM TIDS";
+       case WMI_SERVICE_ARPNS_OFFLOAD:
+               return "ARPNS_OFFLOAD";
+       case WMI_SERVICE_NLO:
+               return "NLO";
+       case WMI_SERVICE_GTK_OFFLOAD:
+               return "GTK_OFFLOAD";
+       case WMI_SERVICE_SCAN_SCH:
+               return "SCAN_SCH";
+       case WMI_SERVICE_CSA_OFFLOAD:
+               return "CSA_OFFLOAD";
+       case WMI_SERVICE_CHATTER:
+               return "CHATTER";
+       case WMI_SERVICE_COEX_FREQAVOID:
+               return "COEX_FREQAVOID";
+       case WMI_SERVICE_PACKET_POWER_SAVE:
+               return "PACKET_POWER_SAVE";
+       case WMI_SERVICE_FORCE_FW_HANG:
+               return "FORCE FW HANG";
+       case WMI_SERVICE_GPIO:
+               return "GPIO";
+       case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM:
+               return "MODULATED DTIM";
+       case WMI_STA_UAPSD_BASIC_AUTO_TRIG:
+               return "BASIC UAPSD";
+       case WMI_STA_UAPSD_VAR_AUTO_TRIG:
+               return "VAR UAPSD";
+       case WMI_SERVICE_STA_KEEP_ALIVE:
+               return "STA KEEP ALIVE";
+       case WMI_SERVICE_TX_ENCAP:
+               return "TX ENCAP";
+       default:
+               return "UNKNOWN SERVICE\n";
+       }
+}
+
+
+#define WMI_SERVICE_BM_SIZE \
+       ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32))
+
+/* 2 word representation of MAC addr */
+struct wmi_mac_addr {
+       union {
+               u8 addr[6];
+               struct {
+                       u32 word0;
+                       u32 word1;
+               } __packed;
+       } __packed;
+} __packed;
+
+/* macro to convert MAC address from WMI word format to char array */
+#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \
+       (c_macaddr)[0] =  ((pwmi_mac_addr)->word0) & 0xff; \
+       (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \
+       (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \
+       (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \
+       (c_macaddr)[4] =  ((pwmi_mac_addr)->word1) & 0xff; \
+       (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
+       } while (0)
+
+/*
+ * wmi command groups.
+ */
+enum wmi_cmd_group {
+       /* 0 to 2 are reserved */
+       WMI_GRP_START = 0x3,
+       WMI_GRP_SCAN = WMI_GRP_START,
+       WMI_GRP_PDEV,
+       WMI_GRP_VDEV,
+       WMI_GRP_PEER,
+       WMI_GRP_MGMT,
+       WMI_GRP_BA_NEG,
+       WMI_GRP_STA_PS,
+       WMI_GRP_DFS,
+       WMI_GRP_ROAM,
+       WMI_GRP_OFL_SCAN,
+       WMI_GRP_P2P,
+       WMI_GRP_AP_PS,
+       WMI_GRP_RATE_CTRL,
+       WMI_GRP_PROFILE,
+       WMI_GRP_SUSPEND,
+       WMI_GRP_BCN_FILTER,
+       WMI_GRP_WOW,
+       WMI_GRP_RTT,
+       WMI_GRP_SPECTRAL,
+       WMI_GRP_STATS,
+       WMI_GRP_ARP_NS_OFL,
+       WMI_GRP_NLO_OFL,
+       WMI_GRP_GTK_OFL,
+       WMI_GRP_CSA_OFL,
+       WMI_GRP_CHATTER,
+       WMI_GRP_TID_ADDBA,
+       WMI_GRP_MISC,
+       WMI_GRP_GPIO,
+};
+
+#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
+#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
+
+/* Command IDs and commande events. */
+enum wmi_cmd_id {
+       WMI_INIT_CMDID = 0x1,
+
+       /* Scan specific commands */
+       WMI_START_SCAN_CMDID = WMI_CMD_GRP(WMI_GRP_SCAN),
+       WMI_STOP_SCAN_CMDID,
+       WMI_SCAN_CHAN_LIST_CMDID,
+       WMI_SCAN_SCH_PRIO_TBL_CMDID,
+
+       /* PDEV (physical device) specific commands */
+       WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_CMD_GRP(WMI_GRP_PDEV),
+       WMI_PDEV_SET_CHANNEL_CMDID,
+       WMI_PDEV_SET_PARAM_CMDID,
+       WMI_PDEV_PKTLOG_ENABLE_CMDID,
+       WMI_PDEV_PKTLOG_DISABLE_CMDID,
+       WMI_PDEV_SET_WMM_PARAMS_CMDID,
+       WMI_PDEV_SET_HT_CAP_IE_CMDID,
+       WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+       WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+       WMI_PDEV_SET_QUIET_MODE_CMDID,
+       WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       WMI_PDEV_GET_TPC_CONFIG_CMDID,
+       WMI_PDEV_SET_BASE_MACADDR_CMDID,
+
+       /* VDEV (virtual device) specific commands */
+       WMI_VDEV_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_VDEV),
+       WMI_VDEV_DELETE_CMDID,
+       WMI_VDEV_START_REQUEST_CMDID,
+       WMI_VDEV_RESTART_REQUEST_CMDID,
+       WMI_VDEV_UP_CMDID,
+       WMI_VDEV_STOP_CMDID,
+       WMI_VDEV_DOWN_CMDID,
+       WMI_VDEV_SET_PARAM_CMDID,
+       WMI_VDEV_INSTALL_KEY_CMDID,
+
+       /* peer specific commands */
+       WMI_PEER_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_PEER),
+       WMI_PEER_DELETE_CMDID,
+       WMI_PEER_FLUSH_TIDS_CMDID,
+       WMI_PEER_SET_PARAM_CMDID,
+       WMI_PEER_ASSOC_CMDID,
+       WMI_PEER_ADD_WDS_ENTRY_CMDID,
+       WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+       WMI_PEER_MCAST_GROUP_CMDID,
+
+       /* beacon/management specific commands */
+       WMI_BCN_TX_CMDID = WMI_CMD_GRP(WMI_GRP_MGMT),
+       WMI_PDEV_SEND_BCN_CMDID,
+       WMI_BCN_TMPL_CMDID,
+       WMI_BCN_FILTER_RX_CMDID,
+       WMI_PRB_REQ_FILTER_RX_CMDID,
+       WMI_MGMT_TX_CMDID,
+       WMI_PRB_TMPL_CMDID,
+
+       /* commands to directly control BA negotiation directly from host. */
+       WMI_ADDBA_CLEAR_RESP_CMDID = WMI_CMD_GRP(WMI_GRP_BA_NEG),
+       WMI_ADDBA_SEND_CMDID,
+       WMI_ADDBA_STATUS_CMDID,
+       WMI_DELBA_SEND_CMDID,
+       WMI_ADDBA_SET_RESP_CMDID,
+       WMI_SEND_SINGLEAMSDU_CMDID,
+
+       /* Station power save specific config */
+       WMI_STA_POWERSAVE_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_STA_PS),
+       WMI_STA_POWERSAVE_PARAM_CMDID,
+       WMI_STA_MIMO_PS_MODE_CMDID,
+
+       /** DFS-specific commands */
+       WMI_PDEV_DFS_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_DFS),
+       WMI_PDEV_DFS_DISABLE_CMDID,
+
+       /* Roaming specific  commands */
+       WMI_ROAM_SCAN_MODE = WMI_CMD_GRP(WMI_GRP_ROAM),
+       WMI_ROAM_SCAN_RSSI_THRESHOLD,
+       WMI_ROAM_SCAN_PERIOD,
+       WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       WMI_ROAM_AP_PROFILE,
+
+       /* offload scan specific commands */
+       WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_CMD_GRP(WMI_GRP_OFL_SCAN),
+       WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+       WMI_OFL_SCAN_PERIOD,
+
+       /* P2P specific commands */
+       WMI_P2P_DEV_SET_DEVICE_INFO = WMI_CMD_GRP(WMI_GRP_P2P),
+       WMI_P2P_DEV_SET_DISCOVERABILITY,
+       WMI_P2P_GO_SET_BEACON_IE,
+       WMI_P2P_GO_SET_PROBE_RESP_IE,
+       WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+
+       /* AP power save specific config */
+       WMI_AP_PS_PEER_PARAM_CMDID = WMI_CMD_GRP(WMI_GRP_AP_PS),
+       WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+       /* Rate-control specific commands */
+       WMI_PEER_RATE_RETRY_SCHED_CMDID =
+       WMI_CMD_GRP(WMI_GRP_RATE_CTRL),
+
+       /* WLAN Profiling commands. */
+       WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_CMD_GRP(WMI_GRP_PROFILE),
+       WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+       /* Suspend resume command Ids */
+       WMI_PDEV_SUSPEND_CMDID = WMI_CMD_GRP(WMI_GRP_SUSPEND),
+       WMI_PDEV_RESUME_CMDID,
+
+       /* Beacon filter commands */
+       WMI_ADD_BCN_FILTER_CMDID = WMI_CMD_GRP(WMI_GRP_BCN_FILTER),
+       WMI_RMV_BCN_FILTER_CMDID,
+
+       /* WOW Specific WMI commands*/
+       WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_CMD_GRP(WMI_GRP_WOW),
+       WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+       WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       WMI_WOW_ENABLE_CMDID,
+       WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+       /* RTT measurement related cmd */
+       WMI_RTT_MEASREQ_CMDID = WMI_CMD_GRP(WMI_GRP_RTT),
+       WMI_RTT_TSF_CMDID,
+
+       /* spectral scan commands */
+       WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_CMD_GRP(WMI_GRP_SPECTRAL),
+       WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+
+       /* F/W stats */
+       WMI_REQUEST_STATS_CMDID = WMI_CMD_GRP(WMI_GRP_STATS),
+
+       /* ARP OFFLOAD REQUEST*/
+       WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_ARP_NS_OFL),
+
+       /* NS offload confid*/
+       WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_NLO_OFL),
+
+       /* GTK offload Specific WMI commands*/
+       WMI_GTK_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_GTK_OFL),
+
+       /* CSA offload Specific WMI commands*/
+       WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_CSA_OFL),
+       WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+
+       /* Chatter commands*/
+       WMI_CHATTER_SET_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_CHATTER),
+
+       /* addba specific commands */
+       WMI_PEER_TID_ADDBA_CMDID = WMI_CMD_GRP(WMI_GRP_TID_ADDBA),
+       WMI_PEER_TID_DELBA_CMDID,
+
+       /* set station mimo powersave method */
+       WMI_STA_DTIM_PS_METHOD_CMDID,
+       /* Configure the Station UAPSD AC Auto Trigger Parameters */
+       WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+
+       /* STA Keep alive parameter configuration,
+          Requires WMI_SERVICE_STA_KEEP_ALIVE */
+       WMI_STA_KEEPALIVE_CMD,
+
+       /* misc command group */
+       WMI_ECHO_CMDID = WMI_CMD_GRP(WMI_GRP_MISC),
+       WMI_PDEV_UTF_CMDID,
+       WMI_DBGLOG_CFG_CMDID,
+       WMI_PDEV_QVIT_CMDID,
+       WMI_PDEV_FTM_INTG_CMDID,
+       WMI_VDEV_SET_KEEPALIVE_CMDID,
+       WMI_VDEV_GET_KEEPALIVE_CMDID,
+
+       /* GPIO Configuration */
+       WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
+       WMI_GPIO_OUTPUT_CMDID,
+};
+
+enum wmi_event_id {
+       WMI_SERVICE_READY_EVENTID = 0x1,
+       WMI_READY_EVENTID,
+
+       /* Scan specific events */
+       WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN),
+
+       /* PDEV specific events */
+       WMI_PDEV_TPC_CONFIG_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PDEV),
+       WMI_CHAN_INFO_EVENTID,
+       WMI_PHYERR_EVENTID,
+
+       /* VDEV specific events */
+       WMI_VDEV_START_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_VDEV),
+       WMI_VDEV_STOPPED_EVENTID,
+       WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
+
+       /* peer specific events */
+       WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER),
+
+       /* beacon/mgmt specific events */
+       WMI_MGMT_RX_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MGMT),
+       WMI_HOST_SWBA_EVENTID,
+       WMI_TBTTOFFSET_UPDATE_EVENTID,
+
+       /* ADDBA Related WMI Events*/
+       WMI_TX_DELBA_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BA_NEG),
+       WMI_TX_ADDBA_COMPLETE_EVENTID,
+
+       /* Roam event to trigger roaming on host */
+       WMI_ROAM_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_ROAM),
+       WMI_PROFILE_MATCH,
+
+       /* WoW */
+       WMI_WOW_WAKEUP_HOST_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_WOW),
+
+       /* RTT */
+       WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_RTT),
+       WMI_TSF_MEASUREMENT_REPORT_EVENTID,
+       WMI_RTT_ERROR_REPORT_EVENTID,
+
+       /* GTK offload */
+       WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GTK_OFL),
+       WMI_GTK_REKEY_FAIL_EVENTID,
+
+       /* CSA IE received event */
+       WMI_CSA_HANDLING_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CSA_OFL),
+
+       /* Misc events */
+       WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC),
+       WMI_PDEV_UTF_EVENTID,
+       WMI_DEBUG_MESG_EVENTID,
+       WMI_UPDATE_STATS_EVENTID,
+       WMI_DEBUG_PRINT_EVENTID,
+       WMI_DCS_INTERFERENCE_EVENTID,
+       WMI_PDEV_QVIT_EVENTID,
+       WMI_WLAN_PROFILE_DATA_EVENTID,
+       WMI_PDEV_FTM_INTG_EVENTID,
+       WMI_WLAN_FREQ_AVOID_EVENTID,
+       WMI_VDEV_GET_KEEPALIVE_EVENTID,
+
+       /* GPIO Event */
+       WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
+};
+
+enum wmi_phy_mode {
+       MODE_11A        = 0,   /* 11a Mode */
+       MODE_11G        = 1,   /* 11b/g Mode */
+       MODE_11B        = 2,   /* 11b Mode */
+       MODE_11GONLY    = 3,   /* 11g only Mode */
+       MODE_11NA_HT20   = 4,  /* 11a HT20 mode */
+       MODE_11NG_HT20   = 5,  /* 11g HT20 mode */
+       MODE_11NA_HT40   = 6,  /* 11a HT40 mode */
+       MODE_11NG_HT40   = 7,  /* 11g HT40 mode */
+       MODE_11AC_VHT20 = 8,
+       MODE_11AC_VHT40 = 9,
+       MODE_11AC_VHT80 = 10,
+       /*    MODE_11AC_VHT160 = 11, */
+       MODE_11AC_VHT20_2G = 11,
+       MODE_11AC_VHT40_2G = 12,
+       MODE_11AC_VHT80_2G = 13,
+       MODE_UNKNOWN    = 14,
+       MODE_MAX        = 14
+};
+
+#define WMI_CHAN_LIST_TAG      0x1
+#define WMI_SSID_LIST_TAG      0x2
+#define WMI_BSSID_LIST_TAG     0x3
+#define WMI_IE_TAG             0x4
+
+struct wmi_channel {
+       __le32 mhz;
+       __le32 band_center_freq1;
+       __le32 band_center_freq2; /* valid for 11ac, 80plus80 */
+       union {
+               __le32 flags; /* WMI_CHAN_FLAG_ */
+               struct {
+                       u8 mode; /* only 6 LSBs */
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo0;
+               struct {
+                       u8 min_power;
+                       u8 max_power;
+                       u8 reg_power;
+                       u8 reg_classid;
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo1;
+               struct {
+                       u8 antenna_max;
+               } __packed;
+       } __packed;
+} __packed;
+
+struct wmi_channel_arg {
+       u32 freq;
+       u32 band_center_freq1;
+       bool passive;
+       bool allow_ibss;
+       bool allow_ht;
+       bool allow_vht;
+       bool ht40plus;
+       /* note: power unit is 1/4th of dBm */
+       u32 min_power;
+       u32 max_power;
+       u32 max_reg_power;
+       u32 max_antenna_gain;
+       u32 reg_class_id;
+       enum wmi_phy_mode mode;
+};
+
+enum wmi_channel_change_cause {
+       WMI_CHANNEL_CHANGE_CAUSE_NONE = 0,
+       WMI_CHANNEL_CHANGE_CAUSE_CSA,
+};
+
+#define WMI_CHAN_FLAG_HT40_PLUS      (1 << 6)
+#define WMI_CHAN_FLAG_PASSIVE        (1 << 7)
+#define WMI_CHAN_FLAG_ADHOC_ALLOWED  (1 << 8)
+#define WMI_CHAN_FLAG_AP_DISABLED    (1 << 9)
+#define WMI_CHAN_FLAG_DFS            (1 << 10)
+#define WMI_CHAN_FLAG_ALLOW_HT       (1 << 11)
+#define WMI_CHAN_FLAG_ALLOW_VHT      (1 << 12)
+
+/* Indicate reason for channel switch */
+#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13)
+
+#define WMI_MAX_SPATIAL_STREAM   3
+
+/* HT Capabilities*/
+#define WMI_HT_CAP_ENABLED                0x0001   /* HT Enabled/ disabled */
+#define WMI_HT_CAP_HT20_SGI       0x0002   /* Short Guard Interval with HT20 */
+#define WMI_HT_CAP_DYNAMIC_SMPS           0x0004   /* Dynamic MIMO powersave */
+#define WMI_HT_CAP_TX_STBC                0x0008   /* B3 TX STBC */
+#define WMI_HT_CAP_TX_STBC_MASK_SHIFT     3
+#define WMI_HT_CAP_RX_STBC                0x0030   /* B4-B5 RX STBC */
+#define WMI_HT_CAP_RX_STBC_MASK_SHIFT     4
+#define WMI_HT_CAP_LDPC                   0x0040   /* LDPC supported */
+#define WMI_HT_CAP_L_SIG_TXOP_PROT        0x0080   /* L-SIG TXOP Protection */
+#define WMI_HT_CAP_MPDU_DENSITY           0x0700   /* MPDU Density */
+#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8
+#define WMI_HT_CAP_HT40_SGI               0x0800
+
+#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED       | \
+                               WMI_HT_CAP_HT20_SGI      | \
+                               WMI_HT_CAP_HT40_SGI      | \
+                               WMI_HT_CAP_TX_STBC       | \
+                               WMI_HT_CAP_RX_STBC       | \
+                               WMI_HT_CAP_LDPC)
+
+
+/*
+ * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information
+ * field. The fields not defined here are not supported, or reserved.
+ * Do not change these masks and if you have to add new one follow the
+ * bitmask as specified by 802.11ac draft.
+ */
+
+#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK            0x00000003
+#define WMI_VHT_CAP_RX_LDPC                      0x00000010
+#define WMI_VHT_CAP_SGI_80MHZ                    0x00000020
+#define WMI_VHT_CAP_TX_STBC                      0x00000080
+#define WMI_VHT_CAP_RX_STBC_MASK                 0x00000300
+#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT           8
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP            0x03800000
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT      23
+#define WMI_VHT_CAP_RX_FIXED_ANT                 0x10000000
+#define WMI_VHT_CAP_TX_FIXED_ANT                 0x20000000
+
+/* The following also refer for max HT AMSDU */
+#define WMI_VHT_CAP_MAX_MPDU_LEN_3839            0x00000000
+#define WMI_VHT_CAP_MAX_MPDU_LEN_7935            0x00000001
+#define WMI_VHT_CAP_MAX_MPDU_LEN_11454           0x00000002
+
+#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454  | \
+                                WMI_VHT_CAP_RX_LDPC             | \
+                                WMI_VHT_CAP_SGI_80MHZ           | \
+                                WMI_VHT_CAP_TX_STBC             | \
+                                WMI_VHT_CAP_RX_STBC_MASK        | \
+                                WMI_VHT_CAP_MAX_AMPDU_LEN_EXP   | \
+                                WMI_VHT_CAP_RX_FIXED_ANT        | \
+                                WMI_VHT_CAP_TX_FIXED_ANT)
+
+/*
+ * Interested readers refer to Rx/Tx MCS Map definition as defined in
+ * 802.11ac
+ */
+#define WMI_VHT_MAX_MCS_4_SS_MASK(r, ss)      ((3 & (r)) << (((ss) - 1) << 1))
+#define WMI_VHT_MAX_SUPP_RATE_MASK           0x1fff0000
+#define WMI_VHT_MAX_SUPP_RATE_MASK_SHIFT     16
+
+enum {
+       REGDMN_MODE_11A              = 0x00001, /* 11a channels */
+       REGDMN_MODE_TURBO            = 0x00002, /* 11a turbo-only channels */
+       REGDMN_MODE_11B              = 0x00004, /* 11b channels */
+       REGDMN_MODE_PUREG            = 0x00008, /* 11g channels (OFDM only) */
+       REGDMN_MODE_11G              = 0x00008, /* XXX historical */
+       REGDMN_MODE_108G             = 0x00020, /* 11a+Turbo channels */
+       REGDMN_MODE_108A             = 0x00040, /* 11g+Turbo channels */
+       REGDMN_MODE_XR               = 0x00100, /* XR channels */
+       REGDMN_MODE_11A_HALF_RATE    = 0x00200, /* 11A half rate channels */
+       REGDMN_MODE_11A_QUARTER_RATE = 0x00400, /* 11A quarter rate channels */
+       REGDMN_MODE_11NG_HT20        = 0x00800, /* 11N-G HT20 channels */
+       REGDMN_MODE_11NA_HT20        = 0x01000, /* 11N-A HT20 channels */
+       REGDMN_MODE_11NG_HT40PLUS    = 0x02000, /* 11N-G HT40 + channels */
+       REGDMN_MODE_11NG_HT40MINUS   = 0x04000, /* 11N-G HT40 - channels */
+       REGDMN_MODE_11NA_HT40PLUS    = 0x08000, /* 11N-A HT40 + channels */
+       REGDMN_MODE_11NA_HT40MINUS   = 0x10000, /* 11N-A HT40 - channels */
+       REGDMN_MODE_11AC_VHT20       = 0x20000, /* 5Ghz, VHT20 */
+       REGDMN_MODE_11AC_VHT40PLUS   = 0x40000, /* 5Ghz, VHT40 + channels */
+       REGDMN_MODE_11AC_VHT40MINUS  = 0x80000, /* 5Ghz  VHT40 - channels */
+       REGDMN_MODE_11AC_VHT80       = 0x100000, /* 5Ghz, VHT80 channels */
+       REGDMN_MODE_ALL              = 0xffffffff
+};
+
+#define REGDMN_CAP1_CHAN_HALF_RATE        0x00000001
+#define REGDMN_CAP1_CHAN_QUARTER_RATE     0x00000002
+#define REGDMN_CAP1_CHAN_HAL49GHZ         0x00000004
+
+/* regulatory capabilities */
+#define REGDMN_EEPROM_EEREGCAP_EN_FCC_MIDBAND   0x0040
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_EVEN    0x0080
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U2         0x0100
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_MIDBAND    0x0200
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_ODD     0x0400
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_NEW_11A    0x0800
+
+struct hal_reg_capabilities {
+       /* regdomain value specified in EEPROM */
+       __le32 eeprom_rd;
+       /*regdomain */
+       __le32 eeprom_rd_ext;
+       /* CAP1 capabilities bit map. */
+       __le32 regcap1;
+       /* REGDMN EEPROM CAP. */
+       __le32 regcap2;
+       /* REGDMN MODE */
+       __le32 wireless_modes;
+       __le32 low_2ghz_chan;
+       __le32 high_2ghz_chan;
+       __le32 low_5ghz_chan;
+       __le32 high_5ghz_chan;
+} __packed;
+
+enum wlan_mode_capability {
+       WHAL_WLAN_11A_CAPABILITY   = 0x1,
+       WHAL_WLAN_11G_CAPABILITY   = 0x2,
+       WHAL_WLAN_11AG_CAPABILITY  = 0x3,
+};
+
+/* structure used by FW for requesting host memory */
+struct wlan_host_mem_req {
+       /* ID of the request */
+       __le32 req_id;
+       /* size of the  of each unit */
+       __le32 unit_size;
+       /* flags to  indicate that
+        * the number units is dependent
+        * on number of resources(num vdevs num peers .. etc)
+        */
+       __le32 num_unit_info;
+       /*
+        * actual number of units to allocate . if flags in the num_unit_info
+        * indicate that number of units is tied to number of a particular
+        * resource to allocate then  num_units filed is set to 0 and host
+        * will derive the number units from number of the resources it is
+        * requesting.
+        */
+       __le32 num_units;
+} __packed;
+
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
+       ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+       (1 << ((svc_id)%(sizeof(u32))))) != 0)
+
+/*
+ * The following struct holds optional payload for
+ * wmi_service_ready_event,e.g., 11ac pass some of the
+ * device capability to the host.
+ */
+struct wmi_service_ready_event {
+       __le32 sw_version;
+       __le32 sw_version_1;
+       __le32 abi_version;
+       /* WMI_PHY_CAPABILITY */
+       __le32 phy_capability;
+       /* Maximum number of frag table entries that SW will populate less 1 */
+       __le32 max_frag_entry;
+       __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+       __le32 num_rf_chains;
+       /*
+        * The following field is only valid for service type
+        * WMI_SERVICE_11AC
+        */
+       __le32 ht_cap_info; /* WMI HT Capability */
+       __le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+       __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+       __le32 hw_min_tx_power;
+       __le32 hw_max_tx_power;
+       struct hal_reg_capabilities hal_reg_capabilities;
+       __le32 sys_cap_info;
+       __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+       /*
+        * Max beacon and Probe Response IE offload size
+        * (includes optional P2P IEs)
+        */
+       __le32 max_bcn_ie_size;
+       /*
+        * request to host to allocate a chuck of memory and pss it down to FW
+        * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+        * data structures. Only valid for low latency interfaces like PCIE
+        * where FW can access this memory directly (or) by DMA.
+        */
+       __le32 num_mem_reqs;
+       struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
+/*
+ * status consists of  upper 16 bits fo int status and lower 16 bits of
+ * module ID that retuned status
+ */
+#define WLAN_INIT_STATUS_SUCCESS   0x0
+#define WLAN_GET_INIT_STATUS_REASON(status)    ((status) & 0xffff)
+#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
+
+#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
+#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
+
+struct wmi_ready_event {
+       __le32 sw_version;
+       __le32 abi_version;
+       struct wmi_mac_addr mac_addr;
+       __le32 status;
+} __packed;
+
+struct wmi_resource_config {
+       /* number of virtual devices (VAPs) to support */
+       __le32 num_vdevs;
+
+       /* number of peer nodes to support */
+       __le32 num_peers;
+
+       /*
+        * In offload mode target supports features like WOW, chatter and
+        * other protocol offloads. In order to support them some
+        * functionalities like reorder buffering, PN checking need to be
+        * done in target. This determines maximum number of peers suported
+        * by target in offload mode
+        */
+       __le32 num_offload_peers;
+
+       /* For target-based RX reordering */
+       __le32 num_offload_reorder_bufs;
+
+       /* number of keys per peer */
+       __le32 num_peer_keys;
+
+       /* total number of TX/RX data TIDs */
+       __le32 num_tids;
+
+       /*
+        * max skid for resolving hash collisions
+        *
+        *   The address search table is sparse, so that if two MAC addresses
+        *   result in the same hash value, the second of these conflicting
+        *   entries can slide to the next index in the address search table,
+        *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+        *   specifies the upper bound on how many subsequent indices to search
+        *   over to find an unoccupied space.
+        */
+       __le32 ast_skid_limit;
+
+       /*
+        * the nominal chain mask for transmit
+        *
+        *   The chain mask may be modified dynamically, e.g. to operate AP
+        *   tx with a reduced number of chains if no clients are associated.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of tx chains.
+        */
+       __le32 tx_chain_mask;
+
+       /*
+        * the nominal chain mask for receive
+        *
+        *   The chain mask may be modified dynamically, e.g. for a client
+        *   to use a reduced number of chains for receive if the traffic to
+        *   the client is low enough that it doesn't require downlink MIMO
+        *   or antenna diversity.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of rx chains.
+        */
+       __le32 rx_chain_mask;
+
+       /*
+        * what rx reorder timeout (ms) to use for the AC
+        *
+        *   Each WMM access class (voice, video, best-effort, background) will
+        *   have its own timeout value to dictate how long to wait for missing
+        *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+        *   already been received.
+        *   This parameter specifies the timeout in milliseconds for each
+        *   class.
+        */
+       __le32 rx_timeout_pri_vi;
+       __le32 rx_timeout_pri_vo;
+       __le32 rx_timeout_pri_be;
+       __le32 rx_timeout_pri_bk;
+
+       /*
+        * what mode the rx should decap packets to
+        *
+        *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+        *   THis setting also determines the default TX behavior, however TX
+        *   behavior can be modified on a per VAP basis during VAP init
+        */
+       __le32 rx_decap_mode;
+
+       /* what is the maximum scan requests than can be queued */
+       __le32 scan_max_pending_reqs;
+
+       /* maximum VDEV that could use BMISS offload */
+       __le32 bmiss_offload_max_vdev;
+
+       /* maximum VDEV that could use offload roaming */
+       __le32 roam_offload_max_vdev;
+
+       /* maximum AP profiles that would push to offload roaming */
+       __le32 roam_offload_max_ap_profiles;
+
+       /*
+        * how many groups to use for mcast->ucast conversion
+        *
+        *   The target's WAL maintains a table to hold information regarding
+        *   which peers belong to a given multicast group, so that if
+        *   multicast->unicast conversion is enabled, the target can convert
+        *   multicast tx frames to a series of unicast tx frames, to each
+        *   peer within the multicast group.
+            This num_mcast_groups configuration parameter tells the target how
+        *   many multicast groups to provide storage for within its multicast
+        *   group membership table.
+        */
+       __le32 num_mcast_groups;
+
+       /*
+        * size to alloc for the mcast membership table
+        *
+        *   This num_mcast_table_elems configuration parameter tells the
+        *   target how many peer elements it needs to provide storage for in
+        *   its multicast group membership table.
+        *   These multicast group membership table elements are shared by the
+        *   multicast groups stored within the table.
+        */
+       __le32 num_mcast_table_elems;
+
+       /*
+        * whether/how to do multicast->unicast conversion
+        *
+        *   This configuration parameter specifies whether the target should
+        *   perform multicast --> unicast conversion on transmit, and if so,
+        *   what to do if it finds no entries in its multicast group
+        *   membership table for the multicast IP address in the tx frame.
+        *   Configuration value:
+        *   0 -> Do not perform multicast to unicast conversion.
+        *   1 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        drop the frame.
+        *   2 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        transmit the frame as multicast.
+        */
+       __le32 mcast2ucast_mode;
+
+       /*
+        * how much memory to allocate for a tx PPDU dbg log
+        *
+        *   This parameter controls how much memory the target will allocate
+        *   to store a log of tx PPDU meta-information (how large the PPDU
+        *   was, when it was sent, whether it was successful, etc.)
+        */
+       __le32 tx_dbg_log_size;
+
+       /* how many AST entries to be allocated for WDS */
+       __le32 num_wds_entries;
+
+       /*
+        * MAC DMA burst size, e.g., For target PCI limit can be
+        * 0 -default, 1 256B
+        */
+       __le32 dma_burst_size;
+
+       /*
+        * Fixed delimiters to be inserted after every MPDU to
+        * account for interface latency to avoid underrun.
+        */
+       __le32 mac_aggr_delim;
+
+       /*
+        *   determine whether target is responsible for detecting duplicate
+        *   non-aggregate MPDU and timing out stale fragments.
+        *
+        *   A-MPDU reordering is always performed on the target.
+        *
+        *   0: target responsible for frag timeout and dup checking
+        *   1: host responsible for frag timeout and dup checking
+        */
+       __le32 rx_skip_defrag_timeout_dup_detection_check;
+
+       /*
+        * Configuration for VoW :
+        * No of Video Nodes to be supported
+        * and Max no of descriptors for each Video link (node).
+        */
+       __le32 vow_config;
+
+       /* maximum VDEV that could use GTK offload */
+       __le32 gtk_offload_max_vdev;
+
+       /* Number of msdu descriptors target should use */
+       __le32 num_msdu_desc;
+
+       /*
+        * Max. number of Tx fragments per MSDU
+        *  This parameter controls the max number of Tx fragments per MSDU.
+        *  This is sent by the target as part of the WMI_SERVICE_READY event
+        *  and is overriden by the OS shim as required.
+        */
+       __le32 max_frag_entries;
+} __packed;
+
+/* strucutre describing host memory chunk. */
+struct host_memory_chunk {
+       /* id of the request that is passed up in service ready */
+       __le32 req_id;
+       /* the physical address the memory chunk */
+       __le32 ptr;
+       /* size of the chunk */
+       __le32 size;
+} __packed;
+
+struct wmi_init_cmd {
+       struct wmi_resource_config resource_config;
+       __le32 num_host_mem_chunks;
+
+       /*
+        * variable number of host memory chunks.
+        * This should be the last element in the structure
+        */
+       struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
+/* TLV for channel list */
+struct wmi_chan_list {
+       __le32 tag; /* WMI_CHAN_LIST_TAG */
+       __le32 num_chan;
+       __le32 channel_list[0];
+} __packed;
+
+struct wmi_bssid_list {
+       __le32 tag; /* WMI_BSSID_LIST_TAG */
+       __le32 num_bssid;
+       struct wmi_mac_addr bssid_list[0];
+} __packed;
+
+struct wmi_ie_data {
+       __le32 tag; /* WMI_IE_TAG */
+       __le32 ie_len;
+       u8 ie_data[0];
+} __packed;
+
+struct wmi_ssid {
+       __le32 ssid_len;
+       u8 ssid[32];
+} __packed;
+
+struct wmi_ssid_list {
+       __le32 tag; /* WMI_SSID_LIST_TAG */
+       __le32 num_ssids;
+       struct wmi_ssid ssids[0];
+} __packed;
+
+/* prefix used by scan requestor ids on the host */
+#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000
+
+/* prefix used by scan request ids generated on the host */
+/* host cycles through the lower 12 bits to generate ids */
+#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000
+
+#define WLAN_SCAN_PARAMS_MAX_SSID    16
+#define WLAN_SCAN_PARAMS_MAX_BSSID   4
+#define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
+
+/* Scan priority numbers must be sequential, starting with 0 */
+enum wmi_scan_priority {
+       WMI_SCAN_PRIORITY_VERY_LOW = 0,
+       WMI_SCAN_PRIORITY_LOW,
+       WMI_SCAN_PRIORITY_MEDIUM,
+       WMI_SCAN_PRIORITY_HIGH,
+       WMI_SCAN_PRIORITY_VERY_HIGH,
+       WMI_SCAN_PRIORITY_COUNT   /* number of priorities supported */
+};
+
+struct wmi_start_scan_cmd {
+       /* Scan ID */
+       __le32 scan_id;
+       /* Scan requestor ID */
+       __le32 scan_req_id;
+       /* VDEV id(interface) that is requesting scan */
+       __le32 vdev_id;
+       /* Scan Priority, input to scan scheduler */
+       __le32 scan_priority;
+       /* Scan events subscription */
+       __le32 notify_scan_events;
+       /* dwell time in msec on active channels */
+       __le32 dwell_time_active;
+       /* dwell time in msec on passive channels */
+       __le32 dwell_time_passive;
+       /*
+        * min time in msec on the BSS channel,only valid if atleast one
+        * VDEV is active
+        */
+       __le32 min_rest_time;
+       /*
+        * max rest time in msec on the BSS channel,only valid if at least
+        * one VDEV is active
+        */
+       /*
+        * the scanner will rest on the bss channel at least min_rest_time
+        * after min_rest_time the scanner will start checking for tx/rx
+        * activity on all VDEVs. if there is no activity the scanner will
+        * switch to off channel. if there is activity the scanner will let
+        * the radio on the bss channel until max_rest_time expires.at
+        * max_rest_time scanner will switch to off channel irrespective of
+        * activity. activity is determined by the idle_time parameter.
+        */
+       __le32 max_rest_time;
+       /*
+        * time before sending next set of probe requests.
+        * The scanner keeps repeating probe requests transmission with
+        * period specified by repeat_probe_time.
+        * The number of probe requests specified depends on the ssid_list
+        * and bssid_list
+        */
+       __le32 repeat_probe_time;
+       /* time in msec between 2 consequetive probe requests with in a set. */
+       __le32 probe_spacing_time;
+       /*
+        * data inactivity time in msec on bss channel that will be used by
+        * scanner for measuring the inactivity.
+        */
+       __le32 idle_time;
+       /* maximum time in msec allowed for scan  */
+       __le32 max_scan_time;
+       /*
+        * delay in msec before sending first probe request after switching
+        * to a channel
+        */
+       __le32 probe_delay;
+       /* Scan control flags */
+       __le32 scan_ctrl_flags;
+
+       /* Burst duration time in msecs */
+       __le32 burst_duration;
+       /*
+        * TLV (tag length value )  paramerters follow the scan_cmd structure.
+        * TLV can contain channel list, bssid list, ssid list and
+        * ie. the TLV tags are defined above;
+        */
+} __packed;
+
+struct wmi_ssid_arg {
+       int len;
+       const u8 *ssid;
+};
+
+struct wmi_bssid_arg {
+       const u8 *bssid;
+};
+
+struct wmi_start_scan_arg {
+       u32 scan_id;
+       u32 scan_req_id;
+       u32 vdev_id;
+       u32 scan_priority;
+       u32 notify_scan_events;
+       u32 dwell_time_active;
+       u32 dwell_time_passive;
+       u32 min_rest_time;
+       u32 max_rest_time;
+       u32 repeat_probe_time;
+       u32 probe_spacing_time;
+       u32 idle_time;
+       u32 max_scan_time;
+       u32 probe_delay;
+       u32 scan_ctrl_flags;
+
+       u32 ie_len;
+       u32 n_channels;
+       u32 n_ssids;
+       u32 n_bssids;
+
+       u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN];
+       u32 channels[64];
+       struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID];
+       struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID];
+};
+
+/* scan control flags */
+
+/* passively scan all channels including active channels */
+#define WMI_SCAN_FLAG_PASSIVE        0x1
+/* add wild card ssid probe request even though ssid_list is specified. */
+#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2
+/* add cck rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_CCK_RATES 0x4
+/* add ofdm rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_OFDM_RATES 0x8
+/* To enable indication of Chan load and Noise floor to host */
+#define WMI_SCAN_CHAN_STAT_EVENT 0x10
+/* Filter Probe request frames  */
+#define WMI_SCAN_FILTER_PROBE_REQ 0x20
+/* When set, DFS channels will not be scanned */
+#define WMI_SCAN_BYPASS_DFS_CHN 0x40
+/* Different FW scan engine may choose to bail out on errors.
+ * Allow the driver to have influence over that. */
+#define WMI_SCAN_CONTINUE_ON_ERROR 0x80
+
+/* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */
+#define WMI_SCAN_CLASS_MASK 0xFF000000
+
+
+enum wmi_stop_scan_type {
+       WMI_SCAN_STOP_ONE       = 0x00000000, /* stop by scan_id */
+       WMI_SCAN_STOP_VDEV_ALL  = 0x01000000, /* stop by vdev_id */
+       WMI_SCAN_STOP_ALL       = 0x04000000, /* stop all scans */
+};
+
+struct wmi_stop_scan_cmd {
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 req_type;
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_stop_scan_arg {
+       u32 req_id;
+       enum wmi_stop_scan_type req_type;
+       union {
+               u32 scan_id;
+               u32 vdev_id;
+       } u;
+};
+
+struct wmi_scan_chan_list_cmd {
+       __le32 num_scan_chans;
+       struct wmi_channel chan_info[0];
+} __packed;
+
+struct wmi_scan_chan_list_arg {
+       u32 n_channels;
+       struct wmi_channel_arg *channels;
+};
+
+enum wmi_bss_filter {
+       WMI_BSS_FILTER_NONE = 0,        /* no beacons forwarded */
+       WMI_BSS_FILTER_ALL,             /* all beacons forwarded */
+       WMI_BSS_FILTER_PROFILE,         /* only beacons matching profile */
+       WMI_BSS_FILTER_ALL_BUT_PROFILE, /* all but beacons matching profile */
+       WMI_BSS_FILTER_CURRENT_BSS,     /* only beacons matching current BSS */
+       WMI_BSS_FILTER_ALL_BUT_BSS,     /* all but beacons matching BSS */
+       WMI_BSS_FILTER_PROBED_SSID,     /* beacons matching probed ssid */
+       WMI_BSS_FILTER_LAST_BSS,        /* marker only */
+};
+
+enum wmi_scan_event_type {
+       WMI_SCAN_EVENT_STARTED         = 0x1,
+       WMI_SCAN_EVENT_COMPLETED       = 0x2,
+       WMI_SCAN_EVENT_BSS_CHANNEL     = 0x4,
+       WMI_SCAN_EVENT_FOREIGN_CHANNEL = 0x8,
+       WMI_SCAN_EVENT_DEQUEUED        = 0x10,
+       WMI_SCAN_EVENT_PREEMPTED       = 0x20, /* possibly by high-prio scan */
+       WMI_SCAN_EVENT_START_FAILED    = 0x40,
+       WMI_SCAN_EVENT_RESTARTED       = 0x80,
+       WMI_SCAN_EVENT_MAX             = 0x8000
+};
+
+enum wmi_scan_completion_reason {
+       WMI_SCAN_REASON_COMPLETED,
+       WMI_SCAN_REASON_CANCELLED,
+       WMI_SCAN_REASON_PREEMPTED,
+       WMI_SCAN_REASON_TIMEDOUT,
+       WMI_SCAN_REASON_MAX,
+};
+
+struct wmi_scan_event {
+       __le32 event_type; /* %WMI_SCAN_EVENT_ */
+       __le32 reason; /* %WMI_SCAN_REASON_ */
+       __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * This defines how much headroom is kept in the
+ * receive frame between the descriptor and the
+ * payload, in order for the WMI PHY error and
+ * management handler to insert header contents.
+ *
+ * This is in bytes.
+ */
+#define WMI_MGMT_RX_HDR_HEADROOM    52
+
+/*
+ * This event will be used for sending scan results
+ * as well as rx mgmt frames to the host. The rx buffer
+ * will be sent as part of this WMI event. It would be a
+ * good idea to pass all the fields in the RX status
+ * descriptor up to the host.
+ */
+struct wmi_mgmt_rx_hdr {
+       __le32 channel;
+       __le32 snr;
+       __le32 rate;
+       __le32 phy_mode;
+       __le32 buf_len;
+       __le32 status; /* %WMI_RX_STATUS_ */
+} __packed;
+
+struct wmi_mgmt_rx_event {
+       struct wmi_mgmt_rx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+#define WMI_RX_STATUS_OK                       0x00
+#define WMI_RX_STATUS_ERR_CRC                  0x01
+#define WMI_RX_STATUS_ERR_DECRYPT              0x08
+#define WMI_RX_STATUS_ERR_MIC                  0x10
+#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS       0x20
+
+struct wmi_single_phyerr_rx_hdr {
+       /* TSF timestamp */
+       __le32 tsf_timestamp;
+
+       /*
+        * Current freq1, freq2
+        *
+        * [7:0]:    freq1[lo]
+        * [15:8] :   freq1[hi]
+        * [23:16]:   freq2[lo]
+        * [31:24]:   freq2[hi]
+        */
+       __le16 freq1;
+       __le16 freq2;
+
+       /*
+        * Combined RSSI over all chains and channel width for this PHY error
+        *
+        * [7:0]: RSSI combined
+        * [15:8]: Channel width (MHz)
+        * [23:16]: PHY error code
+        * [24:16]: reserved (future use)
+        */
+       u8 rssi_combined;
+       u8 chan_width_mhz;
+       u8 phy_err_code;
+       u8 rsvd0;
+
+       /*
+        * RSSI on chain 0 through 3
+        *
+        * This is formatted the same as the PPDU_START RX descriptor
+        * field:
+        *
+        * [7:0]:   pri20
+        * [15:8]:  sec20
+        * [23:16]: sec40
+        * [31:24]: sec80
+        */
+
+       __le32 rssi_chain0;
+       __le32 rssi_chain1;
+       __le32 rssi_chain2;
+       __le32 rssi_chain3;
+
+       /*
+        * Last calibrated NF value for chain 0 through 3
+        *
+        * nf_list_1:
+        *
+        * + [15:0] - chain 0
+        * + [31:16] - chain 1
+        *
+        * nf_list_2:
+        *
+        * + [15:0] - chain 2
+        * + [31:16] - chain 3
+        */
+       __le32 nf_list_1;
+       __le32 nf_list_2;
+
+
+       /* Length of the frame */
+       __le32 buf_len;
+} __packed;
+
+struct wmi_single_phyerr_rx_event {
+       /* Phy error event header */
+       struct wmi_single_phyerr_rx_hdr hdr;
+       /* frame buffer */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_comb_phyerr_rx_hdr {
+       /* Phy error phy error count */
+       __le32 num_phyerr_events;
+       __le32 tsf_l32;
+       __le32 tsf_u32;
+} __packed;
+
+struct wmi_comb_phyerr_rx_event {
+       /* Phy error phy error count */
+       struct wmi_comb_phyerr_rx_hdr hdr;
+       /*
+        * frame buffer - contains multiple payloads in the order:
+        *                    header - payload, header - payload...
+        *  (The header is of type: wmi_single_phyerr_rx_hdr)
+        */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_mgmt_tx_hdr {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 buf_len;
+} __packed;
+
+struct wmi_mgmt_tx_cmd {
+       struct wmi_mgmt_tx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+struct wmi_echo_event {
+       __le32 value;
+} __packed;
+
+struct wmi_echo_cmd {
+       __le32 value;
+} __packed;
+
+
+struct wmi_pdev_set_regdomain_cmd {
+       __le32 reg_domain;
+       __le32 reg_domain_2G;
+       __le32 reg_domain_5G;
+       __le32 conformance_test_limit_2G;
+       __le32 conformance_test_limit_5G;
+} __packed;
+
+/* Command to set/unset chip in quiet mode */
+struct wmi_pdev_set_quiet_cmd {
+       /* period in TUs */
+       __le32 period;
+
+       /* duration in TUs */
+       __le32 duration;
+
+       /* offset in TUs */
+       __le32 next_start;
+
+       /* enable/disable */
+       __le32 enabled;
+} __packed;
+
+
+/*
+ * 802.11g protection mode.
+ */
+enum ath10k_protmode {
+       ATH10K_PROT_NONE     = 0,    /* no protection */
+       ATH10K_PROT_CTSONLY  = 1,    /* CTS to self */
+       ATH10K_PROT_RTSCTS   = 2,    /* RTS-CTS */
+};
+
+enum wmi_beacon_gen_mode {
+       WMI_BEACON_STAGGERED_MODE = 0,
+       WMI_BEACON_BURST_MODE = 1
+};
+
+enum wmi_csa_event_ies_present_flag {
+       WMI_CSA_IE_PRESENT = 0x00000001,
+       WMI_XCSA_IE_PRESENT = 0x00000002,
+       WMI_WBW_IE_PRESENT = 0x00000004,
+       WMI_CSWARP_IE_PRESENT = 0x00000008,
+};
+
+/* wmi CSA receive event from beacon frame */
+struct wmi_csa_event {
+       __le32 i_fc_dur;
+       /* Bit 0-15: FC */
+       /* Bit 16-31: DUR */
+       struct wmi_mac_addr i_addr1;
+       struct wmi_mac_addr i_addr2;
+       __le32 csa_ie[2];
+       __le32 xcsa_ie[2];
+       __le32 wb_ie[2];
+       __le32 cswarp_ie;
+       __le32 ies_present_flag; /* wmi_csa_event_ies_present_flag */
+} __packed;
+
+/* the definition of different PDEV parameters */
+#define PDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
+
+enum wmi_pdev_param {
+       /* TX chian mask */
+       WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+       /* RX chian mask */
+       WMI_PDEV_PARAM_RX_CHAIN_MASK,
+       /* TX power limit for 2G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+       /* TX power limit for 5G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+       /* TX power scale */
+       WMI_PDEV_PARAM_TXPOWER_SCALE,
+       /* Beacon generation mode . 0: host, 1: target   */
+       WMI_PDEV_PARAM_BEACON_GEN_MODE,
+       /* Beacon generation mode . 0: staggered 1: bursted   */
+       WMI_PDEV_PARAM_BEACON_TX_MODE,
+       /*
+        * Resource manager off chan mode .
+        * 0: turn off off chan mode. 1: turn on offchan mode
+        */
+       WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       /*
+        * Protection mode:
+        * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+        */
+       WMI_PDEV_PARAM_PROTECTION_MODE,
+       /* Dynamic bandwidth 0: disable 1: enable */
+       WMI_PDEV_PARAM_DYNAMIC_BW,
+       /* Non aggregrate/ 11g sw retry threshold.0-disable */
+       WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       /* aggregrate sw retry threshold. 0-disable*/
+       WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+       /* Station kickout threshold (non of consecutive failures).0-disable */
+       WMI_PDEV_PARAM_STA_KICKOUT_TH,
+       /* Aggerate size scaling configuration per AC */
+       WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       /* LTR enable */
+       WMI_PDEV_PARAM_LTR_ENABLE,
+       /* LTR latency for BE, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       /* LTR latency for BK, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       /* LTR latency for VI, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       /* LTR latency for VO, in us  */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       /* LTR AC latency timeout, in ms */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       /* LTR platform latency override, in us */
+       WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       /* LTR-RX override, in us */
+       WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+       /* Tx activity timeout for LTR, in us */
+       WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       /* L1SS state machine enable */
+       WMI_PDEV_PARAM_L1SS_ENABLE,
+       /* Deep sleep state machine enable */
+       WMI_PDEV_PARAM_DSLEEP_ENABLE,
+       /* RX buffering flush enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+       /* RX buffering matermark */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK,
+       /* RX buffering timeout enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       /* RX buffering timeout value */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+       /* pdev level stats update period in ms */
+       WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       /* vdev level stats update period in ms */
+       WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       /* peer level stats update period in ms */
+       WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       /* beacon filter status update period */
+       WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+       WMI_PDEV_PARAM_PMF_QOS,
+       /* Access category on which ARP frames are sent */
+       WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+       /* DCS configuration */
+       WMI_PDEV_PARAM_DCS,
+       /* Enable/Disable ANI on target */
+       WMI_PDEV_PARAM_ANI_ENABLE,
+       /* configure the ANI polling period */
+       WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+       /* configure the ANI listening period */
+       WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       /* configure OFDM immunity level */
+       WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+       /* configure CCK immunity level */
+       WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+       /* Enable/Disable CDD for 1x1 STAs in rate control module */
+       WMI_PDEV_PARAM_DYNTXCHAIN,
+       /* Enable/Disable proxy STA */
+       WMI_PDEV_PARAM_PROXY_STA,
+       /* Enable/Disable low power state when all VDEVs are inactive/idle. */
+       WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+       /* Enable/Disable power gating sleep */
+       WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+};
+
+struct wmi_pdev_set_param_cmd {
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+struct wmi_pdev_get_tpc_config_cmd {
+       /* parameter   */
+       __le32 param;
+} __packed;
+
+#define WMI_TPC_RATE_MAX               160
+#define WMI_TPC_TX_N_CHAIN             4
+
+enum wmi_tpc_config_event_flag {
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD     = 0x1,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC    = 0x2,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF    = 0x4,
+};
+
+struct wmi_pdev_tpc_config_event {
+       __le32 reg_domain;
+       __le32 chan_freq;
+       __le32 phy_mode;
+       __le32 twice_antenna_reduction;
+       __le32 twice_max_rd_power;
+       s32 twice_antenna_gain;
+       __le32 power_limit;
+       __le32 rate_max;
+       __le32 num_tx_chain;
+       __le32 ctl;
+       __le32 flags;
+       s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       u8 rates_array[WMI_TPC_RATE_MAX];
+} __packed;
+
+/* Transmit power scale factor. */
+enum wmi_tp_scale {
+       WMI_TP_SCALE_MAX    = 0,        /* no scaling (default) */
+       WMI_TP_SCALE_50     = 1,        /* 50% of max (-3 dBm) */
+       WMI_TP_SCALE_25     = 2,        /* 25% of max (-6 dBm) */
+       WMI_TP_SCALE_12     = 3,        /* 12% of max (-9 dBm) */
+       WMI_TP_SCALE_MIN    = 4,        /* min, but still on   */
+       WMI_TP_SCALE_SIZE   = 5,        /* max num of enum     */
+};
+
+struct wmi_set_channel_cmd {
+       /* channel (only frequency and mode info are used) */
+       struct wmi_channel chan;
+} __packed;
+
+struct wmi_pdev_chanlist_update_event {
+       /* number of channels */
+       __le32 num_chan;
+       /* array of channels */
+       struct wmi_channel channel_list[1];
+} __packed;
+
+#define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32)
+
+struct wmi_debug_mesg_event {
+       /* message buffer, NULL terminated */
+       char bufp[WMI_MAX_DEBUG_MESG];
+} __packed;
+
+enum {
+       /* P2P device */
+       VDEV_SUBTYPE_P2PDEV = 0,
+       /* P2P client */
+       VDEV_SUBTYPE_P2PCLI,
+       /* P2P GO */
+       VDEV_SUBTYPE_P2PGO,
+       /* BT3.0 HS */
+       VDEV_SUBTYPE_BT,
+};
+
+struct wmi_pdev_set_channel_cmd {
+       /* idnore power , only use flags , mode and freq */
+       struct wmi_channel chan;
+} __packed;
+
+/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
+#define WMI_DSCP_MAP_MAX    (64)
+struct wmi_pdev_set_dscp_tid_map_cmd {
+       /* map indicating DSCP to TID conversion */
+       __le32 dscp_to_tid_map[WMI_DSCP_MAP_MAX];
+} __packed;
+
+enum mcast_bcast_rate_id {
+       WMI_SET_MCAST_RATE,
+       WMI_SET_BCAST_RATE
+};
+
+struct mcast_bcast_rate {
+       enum mcast_bcast_rate_id rate_id;
+       __le32 rate;
+} __packed;
+
+struct wmi_wmm_params {
+       __le32 cwmin;
+       __le32 cwmax;
+       __le32 aifs;
+       __le32 txop;
+       __le32 acm;
+       __le32 no_ack;
+} __packed;
+
+struct wmi_pdev_set_wmm_params {
+       struct wmi_wmm_params ac_be;
+       struct wmi_wmm_params ac_bk;
+       struct wmi_wmm_params ac_vi;
+       struct wmi_wmm_params ac_vo;
+} __packed;
+
+struct wmi_wmm_params_arg {
+       u32 cwmin;
+       u32 cwmax;
+       u32 aifs;
+       u32 txop;
+       u32 acm;
+       u32 no_ack;
+};
+
+struct wmi_pdev_set_wmm_params_arg {
+       struct wmi_wmm_params_arg ac_be;
+       struct wmi_wmm_params_arg ac_bk;
+       struct wmi_wmm_params_arg ac_vi;
+       struct wmi_wmm_params_arg ac_vo;
+};
+
+struct wal_dbg_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct wal_dbg_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct wal_dbg_peer_stats {
+       /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+       __le32 dummy;
+} __packed;
+
+struct wal_dbg_stats {
+       struct wal_dbg_tx_stats tx;
+       struct wal_dbg_rx_stats rx;
+       struct wal_dbg_peer_stats peer;
+} __packed;
+
+enum wmi_stats_id {
+       WMI_REQUEST_PEER_STAT   = 0x01,
+       WMI_REQUEST_AP_STAT     = 0x02
+};
+
+struct wmi_request_stats_cmd {
+       __le32 stats_id;
+
+       /*
+        * Space to add parameters like
+        * peer mac addr
+        */
+} __packed;
+
+/* Suspend option */
+enum {
+       /* suspend */
+       WMI_PDEV_SUSPEND,
+
+       /* suspend and disable all interrupts */
+       WMI_PDEV_SUSPEND_AND_DISABLE_INTR,
+};
+
+struct wmi_pdev_suspend_cmd {
+       /* suspend option sent to target */
+       __le32 suspend_opt;
+} __packed;
+
+struct wmi_stats_event {
+       __le32 stats_id; /* %WMI_REQUEST_ */
+       /*
+        * number of pdev stats event structures
+        * (wmi_pdev_stats) 0 or 1
+        */
+       __le32 num_pdev_stats;
+       /*
+        * number of vdev stats event structures
+        * (wmi_vdev_stats) 0 or max vdevs
+        */
+       __le32 num_vdev_stats;
+       /*
+        * number of peer stats event structures
+        * (wmi_peer_stats) 0 or max peers
+        */
+       __le32 num_peer_stats;
+       __le32 num_bcnflt_stats;
+       /*
+        * followed by
+        *   num_pdev_stats * size of(struct wmi_pdev_stats)
+        *   num_vdev_stats * size of(struct wmi_vdev_stats)
+        *   num_peer_stats * size of(struct wmi_peer_stats)
+        *
+        *  By having a zero sized array, the pointer to data area
+        *  becomes available without increasing the struct size
+        */
+       u8 data[0];
+} __packed;
+
+/*
+ * PDEV statistics
+ * TODO: add all PDEV stats here
+ */
+struct wmi_pdev_stats {
+       __le32 chan_nf;        /* Channel noise floor */
+       __le32 tx_frame_count; /* TX frame count */
+       __le32 rx_frame_count; /* RX frame count */
+       __le32 rx_clear_count; /* rx clear count */
+       __le32 cycle_count;    /* cycle count */
+       __le32 phy_err_count;  /* Phy error count */
+       __le32 chan_tx_pwr;    /* channel tx power */
+       struct wal_dbg_stats wal; /* WAL dbg stats */
+} __packed;
+
+/*
+ * VDEV statistics
+ * TODO: add all VDEV stats here
+ */
+struct wmi_vdev_stats {
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * peer statistics.
+ * TODO: add more stats
+ */
+struct wmi_peer_stats {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_tx_rate;
+} __packed;
+
+struct wmi_vdev_create_cmd {
+       __le32 vdev_id;
+       __le32 vdev_type;
+       __le32 vdev_subtype;
+       struct wmi_mac_addr vdev_macaddr;
+} __packed;
+
+enum wmi_vdev_type {
+       WMI_VDEV_TYPE_AP      = 1,
+       WMI_VDEV_TYPE_STA     = 2,
+       WMI_VDEV_TYPE_IBSS    = 3,
+       WMI_VDEV_TYPE_MONITOR = 4,
+};
+
+enum wmi_vdev_subtype {
+       WMI_VDEV_SUBTYPE_NONE       = 0,
+       WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
+       WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
+       WMI_VDEV_SUBTYPE_P2P_GO     = 3,
+};
+
+/* values for vdev_subtype */
+
+/* values for vdev_start_request flags */
+/*
+ * Indicates that AP VDEV uses hidden ssid. only valid for
+ *  AP/GO */
+#define WMI_VDEV_START_HIDDEN_SSID  (1<<0)
+/*
+ * Indicates if robust management frame/management frame
+ *  protection is enabled. For GO/AP vdevs, it indicates that
+ *  it may support station/client associations with RMF enabled.
+ *  For STA/client vdevs, it indicates that sta will
+ *  associate with AP with RMF enabled. */
+#define WMI_VDEV_START_PMF_ENABLED  (1<<1)
+
+struct wmi_p2p_noa_descriptor {
+       __le32 type_count; /* 255: continuous schedule, 0: reserved */
+       __le32 duration;  /* Absent period duration in micro seconds */
+       __le32 interval;   /* Absent period interval in micro seconds */
+       __le32 start_time; /* 32 bit tsf time when in starts */
+} __packed;
+
+struct wmi_vdev_start_request_cmd {
+       /* WMI channel */
+       struct wmi_channel chan;
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* requestor id identifying the caller module */
+       __le32 requestor_id;
+       /* beacon interval from received beacon */
+       __le32 beacon_interval;
+       /* DTIM Period from the received beacon */
+       __le32 dtim_period;
+       /* Flags */
+       __le32 flags;
+       /* ssid field. Only valid for AP/GO/IBSS/BTAmp VDEV type. */
+       struct wmi_ssid ssid;
+       /* beacon/probe reponse xmit rate. Applicable for SoftAP. */
+       __le32 bcn_tx_rate;
+       /* beacon/probe reponse xmit power. Applicable for SoftAP. */
+       __le32 bcn_tx_power;
+       /* number of p2p NOA descriptor(s) from scan entry */
+       __le32 num_noa_descriptors;
+       /*
+        * Disable H/W ack. This used by WMI_VDEV_RESTART_REQUEST_CMDID.
+        * During CAC, Our HW shouldn't ack ditected frames
+        */
+       __le32 disable_hw_ack;
+       /* actual p2p NOA descriptor from scan entry */
+       struct wmi_p2p_noa_descriptor noa_descriptors[2];
+} __packed;
+
+struct wmi_vdev_restart_request_cmd {
+       struct wmi_vdev_start_request_cmd vdev_start_request_cmd;
+} __packed;
+
+struct wmi_vdev_start_request_arg {
+       u32 vdev_id;
+       struct wmi_channel_arg channel;
+       u32 bcn_intval;
+       u32 dtim_period;
+       u8 *ssid;
+       u32 ssid_len;
+       u32 bcn_tx_rate;
+       u32 bcn_tx_power;
+       bool disable_hw_ack;
+       bool hidden_ssid;
+       bool pmf_enabled;
+};
+
+struct wmi_vdev_delete_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_up_cmd {
+       __le32 vdev_id;
+       __le32 vdev_assoc_id;
+       struct wmi_mac_addr vdev_bssid;
+} __packed;
+
+struct wmi_vdev_stop_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_down_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_standby_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_set_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define WMI_MAX_KEY_INDEX   3
+#define WMI_MAX_KEY_LEN     32
+
+#define WMI_KEY_PAIRWISE 0x00
+#define WMI_KEY_GROUP    0x01
+#define WMI_KEY_TX_USAGE 0x02 /* default tx key - static wep */
+
+struct wmi_key_seq_counter {
+       __le32 key_seq_counter_l;
+       __le32 key_seq_counter_h;
+} __packed;
+
+#define WMI_CIPHER_NONE     0x0 /* clear key */
+#define WMI_CIPHER_WEP      0x1
+#define WMI_CIPHER_TKIP     0x2
+#define WMI_CIPHER_AES_OCB  0x3
+#define WMI_CIPHER_AES_CCM  0x4
+#define WMI_CIPHER_WAPI     0x5
+#define WMI_CIPHER_CKIP     0x6
+#define WMI_CIPHER_AES_CMAC 0x7
+
+struct wmi_vdev_install_key_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 key_idx;
+       __le32 key_flags;
+       __le32 key_cipher; /* %WMI_CIPHER_ */
+       struct wmi_key_seq_counter key_rsc_counter;
+       struct wmi_key_seq_counter key_global_rsc_counter;
+       struct wmi_key_seq_counter key_tsc_counter;
+       u8 wpi_key_rsc_counter[16];
+       u8 wpi_key_tsc_counter[16];
+       __le32 key_len;
+       __le32 key_txmic_len;
+       __le32 key_rxmic_len;
+
+       /* contains key followed by tx mic followed by rx mic */
+       u8 key_data[0];
+} __packed;
+
+struct wmi_vdev_install_key_arg {
+       u32 vdev_id;
+       const u8 *macaddr;
+       u32 key_idx;
+       u32 key_flags;
+       u32 key_cipher;
+       u32 key_len;
+       u32 key_txmic_len;
+       u32 key_rxmic_len;
+       const void *key_data;
+};
+
+/* Preamble types to be used with VDEV fixed rate configuration */
+enum wmi_rate_preamble {
+       WMI_RATE_PREAMBLE_OFDM,
+       WMI_RATE_PREAMBLE_CCK,
+       WMI_RATE_PREAMBLE_HT,
+       WMI_RATE_PREAMBLE_VHT,
+};
+
+/* Value to disable fixed rate setting */
+#define WMI_FIXED_RATE_NONE    (0xff)
+
+/* the definition of different VDEV parameters */
+enum wmi_vdev_param {
+       /* RTS Threshold */
+       WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+       /* Fragmentation threshold */
+       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       /* beacon interval in TUs */
+       WMI_VDEV_PARAM_BEACON_INTERVAL,
+       /* Listen interval in TUs */
+       WMI_VDEV_PARAM_LISTEN_INTERVAL,
+       /* muticast rate in Mbps */
+       WMI_VDEV_PARAM_MULTICAST_RATE,
+       /* management frame rate in Mbps */
+       WMI_VDEV_PARAM_MGMT_TX_RATE,
+       /* slot time (long vs short) */
+       WMI_VDEV_PARAM_SLOT_TIME,
+       /* preamble (long vs short) */
+       WMI_VDEV_PARAM_PREAMBLE,
+       /* SWBA time (time before tbtt in msec) */
+       WMI_VDEV_PARAM_SWBA_TIME,
+       /* time period for updating VDEV stats */
+       WMI_VDEV_STATS_UPDATE_PERIOD,
+       /* age out time in msec for frames queued for station in power save */
+       WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+       /*
+        * Host SWBA interval (time in msec before tbtt for SWBA event
+        * generation).
+        */
+       WMI_VDEV_HOST_SWBA_INTERVAL,
+       /* DTIM period (specified in units of num beacon intervals) */
+       WMI_VDEV_PARAM_DTIM_PERIOD,
+       /*
+        * scheduler air time limit for this VDEV. used by off chan
+        * scheduler.
+        */
+       WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       /* enable/dsiable WDS for this VDEV  */
+       WMI_VDEV_PARAM_WDS,
+       /* ATIM Window */
+       WMI_VDEV_PARAM_ATIM_WINDOW,
+       /* BMISS max */
+       WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+       /* BMISS first time */
+       WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+       /* BMISS final time */
+       WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+       /* WMM enables/disabled */
+       WMI_VDEV_PARAM_FEATURE_WMM,
+       /* Channel width */
+       WMI_VDEV_PARAM_CHWIDTH,
+       /* Channel Offset */
+       WMI_VDEV_PARAM_CHEXTOFFSET,
+       /* Disable HT Protection */
+       WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+       /* Quick STA Kickout */
+       WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+       /* Rate to be used with Management frames */
+       WMI_VDEV_PARAM_MGMT_RATE,
+       /* Protection Mode */
+       WMI_VDEV_PARAM_PROTECTION_MODE,
+       /* Fixed rate setting */
+       WMI_VDEV_PARAM_FIXED_RATE,
+       /* Short GI Enable/Disable */
+       WMI_VDEV_PARAM_SGI,
+       /* Enable LDPC */
+       WMI_VDEV_PARAM_LDPC,
+       /* Enable Tx STBC */
+       WMI_VDEV_PARAM_TX_STBC,
+       /* Enable Rx STBC */
+       WMI_VDEV_PARAM_RX_STBC,
+       /* Intra BSS forwarding  */
+       WMI_VDEV_PARAM_INTRA_BSS_FWD,
+       /* Setting Default xmit key for Vdev */
+       WMI_VDEV_PARAM_DEF_KEYID,
+       /* NSS width */
+       WMI_VDEV_PARAM_NSS,
+       /* Set the custom rate for the broadcast data frames */
+       WMI_VDEV_PARAM_BCAST_DATA_RATE,
+       /* Set the custom rate (rate-code) for multicast data frames */
+       WMI_VDEV_PARAM_MCAST_DATA_RATE,
+       /* Tx multicast packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_MCAST_INDICATE,
+       /* Tx DHCP packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_DHCP_INDICATE,
+       /* Enable host inspection of Tx unicast packet to unknown destination */
+       WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+       /* The minimum amount of time AP begins to consider STA inactive */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered inactive when there is no recent
+        * TX/RX activity and no downlink frames are buffered for it. Once a
+        * STA exceeds the maximum idle inactive time, the AP will send an
+        * 802.11 data-null as a keep alive to verify the STA is still
+        * associated. If the STA does ACK the data-null, or if the data-null
+        * is buffered and the STA does not retrieve it, the STA will be
+        * considered unresponsive
+        * (see WMI_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+        */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered unresponsive if there is no recent
+        * TX/RX activity and downlink frames are buffered for it. Once a STA
+        * exceeds the maximum unresponsive time, the AP will send a
+        * WMI_STA_KICKOUT event to the host so the STA can be deleted. */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+       /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+       WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+       /* Enable/Disable RTS-CTS */
+       WMI_VDEV_PARAM_ENABLE_RTSCTS,
+       /* Enable TXBFee/er */
+       WMI_VDEV_PARAM_TXBF,
+
+       /* Set packet power save */
+       WMI_VDEV_PARAM_PACKET_POWERSAVE,
+
+       /*
+        * Drops un-encrypted packets if eceived in an encrypted connection
+        * otherwise forwards to host.
+        */
+       WMI_VDEV_PARAM_DROP_UNENCRY,
+
+       /*
+        * Set the encapsulation type for frames.
+        */
+       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+};
+
+/* slot time long */
+#define WMI_VDEV_SLOT_TIME_LONG                0x1
+/* slot time short */
+#define WMI_VDEV_SLOT_TIME_SHORT       0x2
+/* preablbe long */
+#define WMI_VDEV_PREAMBLE_LONG         0x1
+/* preablbe short */
+#define WMI_VDEV_PREAMBLE_SHORT                0x2
+
+enum wmi_start_event_param {
+       WMI_VDEV_RESP_START_EVENT = 0,
+       WMI_VDEV_RESP_RESTART_EVENT,
+};
+
+struct wmi_vdev_start_response_event {
+       __le32 vdev_id;
+       __le32 req_id;
+       __le32 resp_type; /* %WMI_VDEV_RESP_ */
+       __le32 status;
+} __packed;
+
+struct wmi_vdev_standby_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_stopped_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * common structure used for simple events
+ * (stopped, resume_req, standby response)
+ */
+struct wmi_vdev_simple_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/* VDEV start response status codes */
+/* VDEV succesfully started */
+#define WMI_INIFIED_VDEV_START_RESPONSE_STATUS_SUCCESS 0x0
+
+/* requested VDEV not found */
+#define WMI_INIFIED_VDEV_START_RESPONSE_INVALID_VDEVID 0x1
+
+/* unsupported VDEV combination */
+#define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED  0x2
+
+/* Beacon processing related command and event structures */
+struct wmi_bcn_tx_hdr {
+       __le32 vdev_id;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 bcn_len;
+} __packed;
+
+struct wmi_bcn_tx_cmd {
+       struct wmi_bcn_tx_hdr hdr;
+       u8 *bcn[0];
+} __packed;
+
+struct wmi_bcn_tx_arg {
+       u32 vdev_id;
+       u32 tx_rate;
+       u32 tx_power;
+       u32 bcn_len;
+       const void *bcn;
+};
+
+/* Beacon filter */
+#define WMI_BCN_FILTER_ALL   0 /* Filter all beacons */
+#define WMI_BCN_FILTER_NONE  1 /* Pass all beacons */
+#define WMI_BCN_FILTER_RSSI  2 /* Pass Beacons RSSI >= RSSI threshold */
+#define WMI_BCN_FILTER_BSSID 3 /* Pass Beacons with matching BSSID */
+#define WMI_BCN_FILTER_SSID  4 /* Pass Beacons with matching SSID */
+
+struct wmi_bcn_filter_rx_cmd {
+       /* Filter ID */
+       __le32 bcn_filter_id;
+       /* Filter type - wmi_bcn_filter */
+       __le32 bcn_filter;
+       /* Buffer len */
+       __le32 bcn_filter_len;
+       /* Filter info (threshold, BSSID, RSSI) */
+       u8 *bcn_filter_buf;
+} __packed;
+
+/* Capabilities and IEs to be passed to firmware */
+struct wmi_bcn_prb_info {
+       /* Capabilities */
+       __le32 caps;
+       /* ERP info */
+       __le32 erp;
+       /* Advanced capabilities */
+       /* HT capabilities */
+       /* HT Info */
+       /* ibss_dfs */
+       /* wpa Info */
+       /* rsn Info */
+       /* rrm info */
+       /* ath_ext */
+       /* app IE */
+} __packed;
+
+struct wmi_bcn_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* TIM IE offset from the beginning of the template. */
+       __le32 tim_ie_offset;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* variable length data */
+       u8 data[1];
+} __packed;
+
+struct wmi_prb_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* Variable length data */
+       u8 data[1];
+} __packed;
+
+enum wmi_sta_ps_mode {
+       /* enable power save for the given STA VDEV */
+       WMI_STA_PS_MODE_DISABLED = 0,
+       /* disable power save  for a given STA VDEV */
+       WMI_STA_PS_MODE_ENABLED = 1,
+};
+
+struct wmi_sta_powersave_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /*
+        * Power save mode
+        * (see enum wmi_sta_ps_mode)
+        */
+       __le32 sta_ps_mode;
+} __packed;
+
+enum wmi_csa_offload_en {
+       WMI_CSA_OFFLOAD_DISABLE = 0,
+       WMI_CSA_OFFLOAD_ENABLE = 1,
+};
+
+struct wmi_csa_offload_enable_cmd {
+       __le32 vdev_id;
+       __le32 csa_offload_enable;
+} __packed;
+
+struct wmi_csa_offload_chanswitch_cmd {
+       __le32 vdev_id;
+       struct wmi_channel chan;
+} __packed;
+
+/*
+ * This parameter controls the policy for retrieving frames from AP while the
+ * STA is in sleep state.
+ *
+ * Only takes affect if the sta_ps_mode is enabled
+ */
+enum wmi_sta_ps_param_rx_wake_policy {
+       /*
+        * Wake up when ever there is an  RX activity on the VDEV. In this mode
+        * the Power save SM(state machine) will come out of sleep by either
+        * sending null frame (or) a data frame (with PS==0) in response to TIM
+        * bit set in the received beacon frame from AP.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0,
+
+       /*
+        * Here the power save state machine will not wakeup in response to TIM
+        * bit, instead it will send a PSPOLL (or) UASPD trigger based on UAPSD
+        * configuration setup by WMISET_PS_SET_UAPSD  WMI command.  When all
+        * access categories are delivery-enabled, the station will send a
+        * UAPSD trigger frame, otherwise it will send a PS-Poll.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1,
+};
+
+/*
+ * Number of tx frames/beacon  that cause the power save SM to wake up.
+ *
+ * Value 1 causes the SM to wake up for every TX. Value 0 has a special
+ * meaning, It will cause the SM to never wake up. This is useful if you want
+ * to keep the system to sleep all the time for some kind of test mode . host
+ * can change this parameter any time.  It will affect at the next tx frame.
+ */
+enum wmi_sta_ps_param_tx_wake_threshold {
+       WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0,
+       WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1,
+
+       /*
+        * Values greater than one indicate that many TX attempts per beacon
+        * interval before the STA will wake up
+        */
+};
+
+/*
+ * The maximum number of PS-Poll frames the FW will send in response to
+ * traffic advertised in TIM before waking up (by sending a null frame with PS
+ * = 0). Value 0 has a special meaning: there is no maximum count and the FW
+ * will send as many PS-Poll as are necessary to retrieve buffered BU. This
+ * parameter is used when the RX wake policy is
+ * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake
+ * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE.
+ */
+enum wmi_sta_ps_param_pspoll_count {
+       WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0,
+       /*
+        * Values greater than 0 indicate the maximum numer of PS-Poll frames
+        * FW will send before waking up.
+        */
+};
+
+/*
+ * This will include the delivery and trigger enabled state for every AC.
+ * This is the negotiated state with AP. The host MLME needs to set this based
+ * on AP capability and the state Set in the association request by the
+ * station MLME.Lower 8 bits of the value specify the UAPSD configuration.
+ */
+#define WMI_UAPSD_AC_TYPE_DELI 0
+#define WMI_UAPSD_AC_TYPE_TRIG 1
+
+#define WMI_UAPSD_AC_BIT_MASK(ac, type) \
+       ((type ==  WMI_UAPSD_AC_TYPE_DELI) ? (1<<(ac<<1)) : (1<<((ac<<1)+1)))
+
+enum wmi_sta_ps_param_uapsd {
+       WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+enum wmi_sta_powersave_param {
+       /*
+        * Controls how frames are retrievd from AP while STA is sleeping
+        *
+        * (see enum wmi_sta_ps_param_rx_wake_policy)
+        */
+       WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0,
+
+       /*
+        * The STA will go active after this many TX
+        *
+        * (see enum wmi_sta_ps_param_tx_wake_threshold)
+        */
+       WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1,
+
+       /*
+        * Number of PS-Poll to send before STA wakes up
+        *
+        * (see enum wmi_sta_ps_param_pspoll_count)
+        *
+        */
+       WMI_STA_PS_PARAM_PSPOLL_COUNT = 2,
+
+       /*
+        * TX/RX inactivity time in msec before going to sleep.
+        *
+        * The power save SM will monitor tx/rx activity on the VDEV, if no
+        * activity for the specified msec of the parameter the Power save
+        * SM will go to sleep.
+        */
+       WMI_STA_PS_PARAM_INACTIVITY_TIME = 3,
+
+       /*
+        * Set uapsd configuration.
+        *
+        * (see enum wmi_sta_ps_param_uapsd)
+        */
+       WMI_STA_PS_PARAM_UAPSD = 4,
+};
+
+struct wmi_sta_powersave_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id; /* %WMI_STA_PS_PARAM_ */
+       __le32 param_value;
+} __packed;
+
+/* No MIMO power save */
+#define WMI_STA_MIMO_PS_MODE_DISABLE
+/* mimo powersave mode static*/
+#define WMI_STA_MIMO_PS_MODE_STATIC
+/* mimo powersave mode dynamic */
+#define WMI_STA_MIMO_PS_MODE_DYNAMIC
+
+struct wmi_sta_mimo_ps_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* mimo powersave mode as defined above */
+       __le32 mimo_pwrsave_mode;
+} __packed;
+
+/* U-APSD configuration of peer station from (re)assoc request and TSPECs */
+enum wmi_ap_ps_param_uapsd {
+       WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_AP_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_AP_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_AP_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_AP_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+/* U-APSD maximum service period of peer station */
+enum wmi_ap_ps_peer_param_max_sp {
+       WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3,
+       MAX_WMI_AP_PS_PEER_PARAM_MAX_SP,
+};
+
+/*
+ * AP power save parameter
+ * Set a power save specific parameter for a peer station
+ */
+enum wmi_ap_ps_peer_param {
+       /* Set uapsd configuration for a given peer.
+        *
+        * Include the delivery and trigger enabled state for every AC.
+        * The host  MLME needs to set this based on AP capability and stations
+        * request Set in the association request  received from the station.
+        *
+        * Lower 8 bits of the value specify the UAPSD configuration.
+        *
+        * (see enum wmi_ap_ps_param_uapsd)
+        * The default value is 0.
+        */
+       WMI_AP_PS_PEER_PARAM_UAPSD = 0,
+
+       /*
+        * Set the service period for a UAPSD capable station
+        *
+        * The service period from wme ie in the (re)assoc request frame.
+        *
+        * (see enum wmi_ap_ps_peer_param_max_sp)
+        */
+       WMI_AP_PS_PEER_PARAM_MAX_SP = 1,
+
+       /* Time in seconds for aging out buffered frames for STA in PS */
+       WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2,
+};
+
+struct wmi_ap_ps_peer_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+
+       /* AP powersave param (see enum wmi_ap_ps_peer_param) */
+       __le32 param_id;
+
+       /* AP powersave param value */
+       __le32 param_value;
+} __packed;
+
+/* 128 clients = 4 words */
+#define WMI_TIM_BITMAP_ARRAY_SIZE 4
+
+struct wmi_tim_info {
+       __le32 tim_len;
+       __le32 tim_mcast;
+       __le32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE];
+       __le32 tim_changed;
+       __le32 tim_num_ps_pending;
+} __packed;
+
+/* Maximum number of NOA Descriptors supported */
+#define WMI_P2P_MAX_NOA_DESCRIPTORS 4
+#define WMI_P2P_OPPPS_ENABLE_BIT       BIT(0)
+#define WMI_P2P_OPPPS_CTWINDOW_OFFSET  1
+#define WMI_P2P_NOA_CHANGED_BIT        BIT(0)
+
+struct wmi_p2p_noa_info {
+       /* Bit 0 - Flag to indicate an update in NOA schedule
+          Bits 7-1 - Reserved */
+       u8 changed;
+       /* NOA index */
+       u8 index;
+       /* Bit 0 - Opp PS state of the AP
+          Bits 1-7 - Ctwindow in TUs */
+       u8 ctwindow_oppps;
+       /* Number of NOA descriptors */
+       u8 num_descriptors;
+
+       struct wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
+} __packed;
+
+struct wmi_bcn_info {
+       struct wmi_tim_info tim_info;
+       struct wmi_p2p_noa_info p2p_noa_info;
+} __packed;
+
+struct wmi_host_swba_event {
+       __le32 vdev_map;
+       struct wmi_bcn_info bcn_info[1];
+} __packed;
+
+#define WMI_MAX_AP_VDEV 16
+
+struct wmi_tbtt_offset_event {
+       __le32 vdev_map;
+       __le32 tbttoffset_list[WMI_MAX_AP_VDEV];
+} __packed;
+
+
+struct wmi_peer_create_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_delete_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_flush_tids_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_tid_bitmap;
+} __packed;
+
+struct wmi_fixed_rate {
+       /*
+        * rate mode . 0: disable fixed rate (auto rate)
+        *   1: legacy (non 11n) rate  specified as ieee rate 2*Mbps
+        *   2: ht20 11n rate  specified as mcs index
+        *   3: ht40 11n rate  specified as mcs index
+        */
+       __le32  rate_mode;
+       /*
+        * 4 rate values for 4 rate series. series 0 is stored in byte 0 (LSB)
+        * and series 3 is stored at byte 3 (MSB)
+        */
+       __le32  rate_series;
+       /*
+        * 4 retry counts for 4 rate series. retry count for rate 0 is stored
+        * in byte 0 (LSB) and retry count for rate 3 is stored at byte 3
+        * (MSB)
+        */
+       __le32  rate_retries;
+} __packed;
+
+struct wmi_peer_fixed_rate_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* fixed rate */
+       struct wmi_fixed_rate peer_fixed_rate;
+} __packed;
+
+#define WMI_MGMT_TID    17
+
+struct wmi_addba_clear_resp_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_addba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Buffer/Window size*/
+       __le32 buffersize;
+} __packed;
+
+struct wmi_delba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Is Initiator */
+       __le32 initiator;
+       /* Reason code */
+       __le32 reasoncode;
+} __packed;
+
+struct wmi_addba_setresponse_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* status code */
+       __le32 statuscode;
+} __packed;
+
+struct wmi_send_singleamsdu_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+} __packed;
+
+enum wmi_peer_smps_state {
+       WMI_PEER_SMPS_PS_NONE = 0x0,
+       WMI_PEER_SMPS_STATIC  = 0x1,
+       WMI_PEER_SMPS_DYNAMIC = 0x2
+};
+
+enum wmi_peer_param {
+       WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
+       WMI_PEER_AMPDU      = 0x2,
+       WMI_PEER_AUTHORIZE  = 0x3,
+       WMI_PEER_CHAN_WIDTH = 0x4,
+       WMI_PEER_NSS        = 0x5,
+       WMI_PEER_USE_4ADDR  = 0x6
+};
+
+struct wmi_peer_set_param_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define MAX_SUPPORTED_RATES 128
+
+struct wmi_rate_set {
+       /* total number of rates */
+       __le32 num_rates;
+       /*
+        * rates (each 8bit value) packed into a 32 bit word.
+        * the rates are filled from least significant byte to most
+        * significant byte.
+        */
+       __le32 rates[(MAX_SUPPORTED_RATES/4)+1];
+} __packed;
+
+struct wmi_rate_set_arg {
+       unsigned int num_rates;
+       u8 rates[MAX_SUPPORTED_RATES];
+};
+
+/*
+ * NOTE: It would bea good idea to represent the Tx MCS
+ * info in one word and Rx in another word. This is split
+ * into multiple words for convenience
+ */
+struct wmi_vht_rate_set {
+       __le32 rx_max_rate; /* Max Rx data rate */
+       __le32 rx_mcs_set;  /* Negotiated RX VHT rates */
+       __le32 tx_max_rate; /* Max Tx data rate */
+       __le32 tx_mcs_set;  /* Negotiated TX VHT rates */
+} __packed;
+
+struct wmi_vht_rate_set_arg {
+       u32 rx_max_rate;
+       u32 rx_mcs_set;
+       u32 tx_max_rate;
+       u32 tx_mcs_set;
+};
+
+struct wmi_peer_set_rates_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* legacy rate set */
+       struct wmi_rate_set peer_legacy_rates;
+       /* ht rate set */
+       struct wmi_rate_set peer_ht_rates;
+} __packed;
+
+struct wmi_peer_set_q_empty_callback_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       __le32 callback_enable;
+} __packed;
+
+#define WMI_PEER_AUTH           0x00000001
+#define WMI_PEER_QOS            0x00000002
+#define WMI_PEER_NEED_PTK_4_WAY 0x00000004
+#define WMI_PEER_NEED_GTK_2_WAY 0x00000010
+#define WMI_PEER_APSD           0x00000800
+#define WMI_PEER_HT             0x00001000
+#define WMI_PEER_40MHZ          0x00002000
+#define WMI_PEER_STBC           0x00008000
+#define WMI_PEER_LDPC           0x00010000
+#define WMI_PEER_DYN_MIMOPS     0x00020000
+#define WMI_PEER_STATIC_MIMOPS  0x00040000
+#define WMI_PEER_SPATIAL_MUX    0x00200000
+#define WMI_PEER_VHT            0x02000000
+#define WMI_PEER_80MHZ          0x04000000
+#define WMI_PEER_PMF            0x08000000
+
+/*
+ * Peer rate capabilities.
+ *
+ * This is of interest to the ratecontrol
+ * module which resides in the firmware. The bit definitions are
+ * consistent with that defined in if_athrate.c.
+ */
+#define WMI_RC_DS_FLAG          0x01
+#define WMI_RC_CW40_FLAG        0x02
+#define WMI_RC_SGI_FLAG         0x04
+#define WMI_RC_HT_FLAG          0x08
+#define WMI_RC_RTSCTS_FLAG      0x10
+#define WMI_RC_TX_STBC_FLAG     0x20
+#define WMI_RC_RX_STBC_FLAG     0xC0
+#define WMI_RC_RX_STBC_FLAG_S   6
+#define WMI_RC_WEP_TKIP_FLAG    0x100
+#define WMI_RC_TS_FLAG          0x200
+#define WMI_RC_UAPSD_FLAG       0x400
+
+/* Maximum listen interval supported by hw in units of beacon interval */
+#define ATH10K_MAX_HW_LISTEN_INTERVAL 5
+
+struct wmi_peer_assoc_complete_cmd {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 vdev_id;
+       __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */
+       __le32 peer_associd; /* 16 LSBs */
+       __le32 peer_flags;
+       __le32 peer_caps; /* 16 LSBs */
+       __le32 peer_listen_intval;
+       __le32 peer_ht_caps;
+       __le32 peer_max_mpdu;
+       __le32 peer_mpdu_density; /* 0..16 */
+       __le32 peer_rate_caps;
+       struct wmi_rate_set peer_legacy_rates;
+       struct wmi_rate_set peer_ht_rates;
+       __le32 peer_nss; /* num of spatial streams */
+       __le32 peer_vht_caps;
+       __le32 peer_phymode;
+       struct wmi_vht_rate_set peer_vht_rates;
+       /* HT Operation Element of the peer. Five bytes packed in 2
+        *  INT32 array and filled from lsb to msb. */
+       __le32 peer_ht_info[2];
+} __packed;
+
+struct wmi_peer_assoc_complete_arg {
+       u8 addr[ETH_ALEN];
+       u32 vdev_id;
+       bool peer_reassoc;
+       u16 peer_aid;
+       u32 peer_flags; /* see %WMI_PEER_ */
+       u16 peer_caps;
+       u32 peer_listen_intval;
+       u32 peer_ht_caps;
+       u32 peer_max_mpdu;
+       u32 peer_mpdu_density; /* 0..16 */
+       u32 peer_rate_caps; /* see %WMI_RC_ */
+       struct wmi_rate_set_arg peer_legacy_rates;
+       struct wmi_rate_set_arg peer_ht_rates;
+       u32 peer_num_spatial_streams;
+       u32 peer_vht_caps;
+       enum wmi_phy_mode peer_phymode;
+       struct wmi_vht_rate_set_arg peer_vht_rates;
+};
+
+struct wmi_peer_add_wds_entry_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_remove_wds_entry_cmd {
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_q_empty_callback_event {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+/*
+ * Channel info WMI event
+ */
+struct wmi_chan_info_event {
+       __le32 err_code;
+       __le32 freq;
+       __le32 cmd_flags;
+       __le32 noise_floor;
+       __le32 rx_clear_count;
+       __le32 cycle_count;
+} __packed;
+
+/* 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)
+
+struct bss_bcn_stats {
+       __le32 vdev_id;
+       __le32 bss_bcnsdropped;
+       __le32 bss_bcnsdelivered;
+} __packed;
+
+struct bcn_filter_stats {
+       __le32 bcns_dropped;
+       __le32 bcns_delivered;
+       __le32 activefilters;
+       struct bss_bcn_stats bss_stats;
+} __packed;
+
+struct wmi_add_bcn_filter_cmd {
+       u32 vdev_id;
+       u32 ie_map[BCN_FLT_MAX_ELEMS_IE_LIST];
+} __packed;
+
+enum wmi_sta_keepalive_method {
+       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1,
+       WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
+};
+
+/* note: ip4 addresses are in network byte order, i.e. big endian */
+struct wmi_sta_keepalive_arp_resp {
+       __be32 src_ip4_addr;
+       __be32 dest_ip4_addr;
+       struct wmi_mac_addr dest_mac_addr;
+} __packed;
+
+struct wmi_sta_keepalive_cmd {
+       __le32 vdev_id;
+       __le32 enabled;
+       __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
+       __le32 interval; /* in seconds */
+       struct wmi_sta_keepalive_arp_resp arp_resp;
+} __packed;
+
+#define ATH10K_RTS_MAX         2347
+#define ATH10K_FRAGMT_THRESHOLD_MIN    540
+#define ATH10K_FRAGMT_THRESHOLD_MAX    2346
+
+#define WMI_MAX_EVENT 0x1000
+/* Maximum number of pending TXed WMI packets */
+#define WMI_MAX_PENDING_TX_COUNT 128
+#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
+
+/* By default disable power save for IBSS */
+#define ATH10K_DEFAULT_ATIM 0
+
+struct ath10k;
+struct ath10k_vif;
+
+int ath10k_wmi_attach(struct ath10k *ar);
+void ath10k_wmi_detach(struct ath10k *ar);
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
+void ath10k_wmi_flush_tx(struct ath10k *ar);
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value);
+int ath10k_wmi_cmd_init(struct ath10k *ar);
+int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
+void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
+int ath10k_wmi_stop_scan(struct ath10k *ar,
+                        const struct wmi_stop_scan_arg *arg);
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN]);
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                           const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+                      const u8 *bssid);
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value);
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg);
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                  const u8 peer_addr[ETH_ALEN], u32 tid_bitmap);
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr,
+                             enum wmi_peer_param param_id, u32 param_value);
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg);
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode);
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value);
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value);
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg);
+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);
+
+#endif /* _WMI_H_ */
index 7f702fe..ce67ab7 100644 (file)
@@ -60,6 +60,7 @@
 
 #include <asm/unaligned.h>
 
+#include <net/mac80211.h>
 #include "base.h"
 #include "reg.h"
 #include "debug.h"
@@ -666,9 +667,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
        return htype;
 }
 
+static struct ieee80211_rate *
+ath5k_get_rate(const struct ieee80211_hw *hw,
+              const struct ieee80211_tx_info *info,
+              struct ath5k_buf *bf, int idx)
+{
+       /*
+       * convert a ieee80211_tx_rate RC-table entry to
+       * the respective ieee80211_rate struct
+       */
+       if (bf->rates[idx].idx < 0) {
+               return NULL;
+       }
+
+       return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ];
+}
+
+static u16
+ath5k_get_rate_hw_value(const struct ieee80211_hw *hw,
+                       const struct ieee80211_tx_info *info,
+                       struct ath5k_buf *bf, int idx)
+{
+       struct ieee80211_rate *rate;
+       u16 hw_rate;
+       u8 rc_flags;
+
+       rate = ath5k_get_rate(hw, info, bf, idx);
+       if (!rate)
+               return 0;
+
+       rc_flags = bf->rates[idx].flags;
+       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+                  rate->hw_value_short : rate->hw_value;
+
+       return hw_rate;
+}
+
 static int
 ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
-                 struct ath5k_txq *txq, int padsize)
+                 struct ath5k_txq *txq, int padsize,
+                 struct ieee80211_tx_control *control)
 {
        struct ath5k_desc *ds = bf->desc;
        struct sk_buff *skb = bf->skb;
@@ -688,7 +726,11 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
                        DMA_TO_DEVICE);
 
-       rate = ieee80211_get_tx_rate(ah->hw, info);
+       ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates,
+                              ARRAY_SIZE(bf->rates));
+
+       rate = ath5k_get_rate(ah->hw, info, bf, 0);
+
        if (!rate) {
                ret = -EINVAL;
                goto err_unmap;
@@ -698,8 +740,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                flags |= AR5K_TXDESC_NOACK;
 
        rc_flags = info->control.rates[0].flags;
-       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
-               rate->hw_value_short : rate->hw_value;
+
+       hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
 
        pktlen = skb->len;
 
@@ -722,12 +764,13 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
                        info->control.vif, pktlen, info));
        }
+
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
                ieee80211_get_hdrlen_from_skb(skb), padsize,
                get_hw_packet_type(skb),
                (ah->ah_txpower.txp_requested * 2),
                hw_rate,
-               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+               bf->rates[0].count, keyidx, ah->ah_tx_ant, flags,
                cts_rate, duration);
        if (ret)
                goto err_unmap;
@@ -736,13 +779,15 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        if (ah->ah_capabilities.cap_has_mrr_support) {
                memset(mrr_rate, 0, sizeof(mrr_rate));
                memset(mrr_tries, 0, sizeof(mrr_tries));
+
                for (i = 0; i < 3; i++) {
-                       rate = ieee80211_get_alt_retry_rate(ah->hw, info, i);
+
+                       rate = ath5k_get_rate(ah->hw, info, bf, i);
                        if (!rate)
                                break;
 
-                       mrr_rate[i] = rate->hw_value;
-                       mrr_tries[i] = info->control.rates[i + 1].count;
+                       mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i);
+                       mrr_tries[i] = bf->rates[i].count;
                }
 
                ath5k_hw_setup_mrr_tx_desc(ah, ds,
@@ -1515,7 +1560,7 @@ unlock:
 
 void
 ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-              struct ath5k_txq *txq)
+              struct ath5k_txq *txq, struct ieee80211_tx_control *control)
 {
        struct ath5k_hw *ah = hw->priv;
        struct ath5k_buf *bf;
@@ -1555,7 +1600,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
 
        bf->skb = skb;
 
-       if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
+       if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) {
                bf->skb = NULL;
                spin_lock_irqsave(&ah->txbuflock, flags);
                list_add_tail(&bf->list, &ah->txbuf);
@@ -1571,11 +1616,13 @@ drop_packet:
 
 static void
 ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
-                        struct ath5k_txq *txq, struct ath5k_tx_status *ts)
+                        struct ath5k_txq *txq, struct ath5k_tx_status *ts,
+                        struct ath5k_buf *bf)
 {
        struct ieee80211_tx_info *info;
        u8 tries[3];
        int i;
+       int size = 0;
 
        ah->stats.tx_all_count++;
        ah->stats.tx_bytes_count += skb->len;
@@ -1587,6 +1634,9 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
 
        ieee80211_tx_info_clear_status(info);
 
+       size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
+       memcpy(info->status.rates, bf->rates, size);
+
        for (i = 0; i < ts->ts_final_idx; i++) {
                struct ieee80211_tx_rate *r =
                        &info->status.rates[i];
@@ -1663,7 +1713,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
 
                        dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
                                        DMA_TO_DEVICE);
-                       ath5k_tx_frame_completed(ah, skb, txq, &ts);
+                       ath5k_tx_frame_completed(ah, skb, txq, &ts, bf);
                }
 
                /*
@@ -1917,7 +1967,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
 
        skb = ieee80211_get_buffered_bc(ah->hw, vif);
        while (skb) {
-               ath5k_tx_queue(ah->hw, skb, ah->cabq);
+               ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL);
 
                if (ah->cabq->txq_len >= ah->cabq->txq_max)
                        break;
@@ -2442,7 +2492,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
                        IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                        IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_MFP_CAPABLE |
-                       IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_SUPPORTS_RC_TABLE;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_AP) |
index 6c94c7f..ca9a83c 100644 (file)
@@ -47,6 +47,7 @@ struct ath5k_hw;
 struct ath5k_txq;
 struct ieee80211_channel;
 struct ath_bus_ops;
+struct ieee80211_tx_control;
 enum nl80211_iftype;
 
 enum ath5k_srev_type {
@@ -61,11 +62,12 @@ struct ath5k_srev_name {
 };
 
 struct ath5k_buf {
-       struct list_head        list;
-       struct ath5k_desc       *desc;  /* virtual addr of desc */
-       dma_addr_t              daddr;  /* physical addr of desc */
-       struct sk_buff          *skb;   /* skbuff for buf */
-       dma_addr_t              skbaddr;/* physical addr of skb data */
+       struct list_head                list;
+       struct ath5k_desc               *desc;          /* virtual addr of desc */
+       dma_addr_t                      daddr;          /* physical addr of desc */
+       struct sk_buff                  *skb;           /* skbuff for buf */
+       dma_addr_t                      skbaddr;        /* physical addr of skb data */
+       struct ieee80211_tx_rate        rates[4];       /* number of multi-rate stages */
 };
 
 struct ath5k_vif {
@@ -103,7 +105,7 @@ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
 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,
-                   struct ath5k_txq *txq);
+                   struct ath5k_txq *txq, struct ieee80211_tx_control *control);
 
 const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
 
index 06f86f4..81b686c 100644 (file)
@@ -66,7 +66,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
                return;
        }
 
-       ath5k_tx_queue(hw, skb, &ah->txqs[qnum]);
+       ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control);
 }
 
 
index 5c9736a..2437ad2 100644 (file)
@@ -3175,10 +3175,21 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
        struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
        struct ath6kl *ar = ath6kl_priv(vif->ndev);
-       u32 id;
+       u32 id, freq;
        const struct ieee80211_mgmt *mgmt;
        bool more_data, queued;
 
+       /* default to the current channel, but use the one specified as argument
+        * if any
+        */
+       freq = vif->ch_hint;
+       if (chan)
+               freq = chan->center_freq;
+
+       /* never send freq zero to the firmware */
+       if (WARN_ON(freq == 0))
+               return -EINVAL;
+
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
            ieee80211_is_probe_resp(mgmt->frame_control) &&
@@ -3188,8 +3199,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                 * command to allow the target to fill in the generic IEs.
                 */
                *cookie = 0; /* TX status not supported */
-               return ath6kl_send_go_probe_resp(vif, buf, len,
-                                                chan->center_freq);
+               return ath6kl_send_go_probe_resp(vif, buf, len, freq);
        }
 
        id = vif->send_action_id++;
@@ -3205,17 +3215,14 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        /* AP mode Power saving processing */
        if (vif->nw_type == AP_NETWORK) {
-               queued = ath6kl_mgmt_powersave_ap(vif,
-                                       id, chan->center_freq,
-                                       wait, buf,
-                                       len, &more_data, no_cck);
+               queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len,
+                                                 &more_data, no_cck);
                if (queued)
                        return 0;
        }
 
-       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
-                                       chan->center_freq, wait,
-                                       buf, len, no_cck);
+       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq,
+                                       wait, buf, len, no_cck);
 }
 
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
@@ -3679,6 +3686,20 @@ err:
        return NULL;
 }
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath6kl_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT |
+                WIPHY_WOWLAN_DISCONNECT |
+                WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
+                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
+                WIPHY_WOWLAN_4WAY_HANDSHAKE,
+       .n_patterns = WOW_MAX_FILTERS_PER_LIST,
+       .pattern_min_len = 1,
+       .pattern_max_len = WOW_PATTERN_SIZE,
+};
+#endif
+
 int ath6kl_cfg80211_init(struct ath6kl *ar)
 {
        struct wiphy *wiphy = ar->wiphy;
@@ -3772,15 +3793,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
        wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                             WIPHY_WOWLAN_DISCONNECT |
-                             WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
-                             WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                             WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
-                             WIPHY_WOWLAN_4WAY_HANDSHAKE;
-       wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
+       wiphy->wowlan = &ath6kl_wowlan_support;
 #endif
 
        wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
index fe38b83..dbfd17d 100644 (file)
@@ -1240,20 +1240,14 @@ static ssize_t ath6kl_force_roam_write(struct file *file,
        char buf[20];
        size_t len;
        u8 bssid[ETH_ALEN];
-       int i;
-       int addr[ETH_ALEN];
 
        len = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
        buf[len] = '\0';
 
-       if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
-                  &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5])
-           != ETH_ALEN)
+       if (!mac_pton(buf, bssid))
                return -EINVAL;
-       for (i = 0; i < ETH_ALEN; i++)
-               bssid[i] = addr[i];
 
        ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
        if (ret)
index 40ffee6..6a67881 100644 (file)
@@ -1696,10 +1696,16 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                                                    test_bit(WMI_READY,
                                                             &ar->flag),
                                                    WMI_TIMEOUT);
+       if (timeleft <= 0) {
+               clear_bit(WMI_READY, &ar->flag);
+               ath6kl_err("wmi is not ready or wait was interrupted: %ld\n",
+                          timeleft);
+               ret = -EIO;
+               goto err_htc_stop;
+       }
 
        ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
 
-
        if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
                ath6kl_info("%s %s fw %s api %d%s\n",
                            ar->hw.name,
@@ -1718,12 +1724,6 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                goto err_htc_stop;
        }
 
-       if (!timeleft || signal_pending(current)) {
-               ath6kl_err("wmi is not ready or wait was interrupted\n");
-               ret = -EIO;
-               goto err_htc_stop;
-       }
-
        ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
 
        /* communicate the wmi protocol verision to the target */
index fb14145..7126bdd 100644 (file)
@@ -345,17 +345,17 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
 {
        struct hif_scatter_req *s_req;
        struct bus_request *bus_req;
-       int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+       int i, scat_req_sz, scat_list_sz, size;
        u8 *virt_buf;
 
        scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
        scat_req_sz = sizeof(*s_req) + scat_list_sz;
 
        if (!virt_scat)
-               sg_sz = sizeof(struct scatterlist) * n_scat_entry;
+               size = sizeof(struct scatterlist) * n_scat_entry;
        else
-               buf_sz =  2 * L1_CACHE_BYTES +
-                         ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+               size =  2 * L1_CACHE_BYTES +
+                       ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
 
        for (i = 0; i < n_scat_req; i++) {
                /* allocate the scatter request */
@@ -364,7 +364,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                        return -ENOMEM;
 
                if (virt_scat) {
-                       virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+                       virt_buf = kzalloc(size, GFP_KERNEL);
                        if (!virt_buf) {
                                kfree(s_req);
                                return -ENOMEM;
@@ -374,7 +374,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                                (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
                } else {
                        /* allocate sglist */
-                       s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+                       s_req->sgentries = kzalloc(size, GFP_KERNEL);
 
                        if (!s_req->sgentries) {
                                kfree(s_req);
index bed0d33..f38ff6a 100644 (file)
@@ -1061,6 +1061,22 @@ static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar)
        return;
 }
 
+static int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       /*
+        * cfg80211 suspend/WOW currently not supported for USB.
+        */
+       return 0;
+}
+
+static int ath6kl_usb_resume(struct ath6kl *ar)
+{
+       /*
+        * cfg80211 resume currently not supported for USB.
+        */
+       return 0;
+}
+
 static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .diag_read32 = ath6kl_usb_diag_read32,
        .diag_write32 = ath6kl_usb_diag_write32,
@@ -1074,6 +1090,8 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .pipe_map_service = ath6kl_usb_map_service_pipe,
        .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number,
        .cleanup_scatter = ath6kl_usb_cleanup_scatter,
+       .suspend = ath6kl_usb_suspend,
+       .resume = ath6kl_usb_resume,
 };
 
 /* ath6kl usb driver registered functions */
@@ -1152,7 +1170,7 @@ static void ath6kl_usb_remove(struct usb_interface *interface)
 
 #ifdef CONFIG_PM
 
-static int ath6kl_usb_suspend(struct usb_interface *interface,
+static int ath6kl_usb_pm_suspend(struct usb_interface *interface,
                              pm_message_t message)
 {
        struct ath6kl_usb *device;
@@ -1162,7 +1180,7 @@ static int ath6kl_usb_suspend(struct usb_interface *interface,
        return 0;
 }
 
-static int ath6kl_usb_resume(struct usb_interface *interface)
+static int ath6kl_usb_pm_resume(struct usb_interface *interface)
 {
        struct ath6kl_usb *device;
        device = usb_get_intfdata(interface);
@@ -1175,7 +1193,7 @@ static int ath6kl_usb_resume(struct usb_interface *interface)
        return 0;
 }
 
-static int ath6kl_usb_reset_resume(struct usb_interface *intf)
+static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
 {
        if (usb_get_intfdata(intf))
                ath6kl_usb_remove(intf);
@@ -1184,9 +1202,9 @@ static int ath6kl_usb_reset_resume(struct usb_interface *intf)
 
 #else
 
-#define ath6kl_usb_suspend NULL
-#define ath6kl_usb_resume NULL
-#define ath6kl_usb_reset_resume NULL
+#define ath6kl_usb_pm_suspend NULL
+#define ath6kl_usb_pm_resume NULL
+#define ath6kl_usb_pm_reset_resume NULL
 
 #endif
 
@@ -1201,9 +1219,9 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids);
 static struct usb_driver ath6kl_usb_driver = {
        .name = "ath6kl_usb",
        .probe = ath6kl_usb_probe,
-       .suspend = ath6kl_usb_suspend,
-       .resume = ath6kl_usb_resume,
-       .reset_resume = ath6kl_usb_reset_resume,
+       .suspend = ath6kl_usb_pm_suspend,
+       .resume = ath6kl_usb_pm_resume,
+       .reset_resume = ath6kl_usb_pm_reset_resume,
        .disconnect = ath6kl_usb_remove,
        .id_table = ath6kl_usb_ids,
        .supports_autosuspend = true,
index e91725b..4994bea 100644 (file)
@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
        {  5,  4,  1  }, /* lvl 5 */
        {  6,  5,  1  }, /* lvl 6 */
        {  7,  6,  1  }, /* lvl 7 */
-       {  7,  6,  0  }, /* lvl 8 */
-       {  7,  7,  0  }  /* lvl 9 */
+       {  7,  7,  1  }, /* lvl 8 */
+       {  7,  8,  0  }  /* lvl 9 */
 };
 #define ATH9K_ANI_OFDM_NUM_LEVEL \
        ARRAY_SIZE(ofdm_level_table)
@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = {
        {  4,  0  }, /* lvl 4 */
        {  5,  0  }, /* lvl 5 */
        {  6,  0  }, /* lvl 6 */
-       {  6,  0  }, /* lvl 7 (only for high rssi) */
-       {  7,  0  }  /* lvl 8 (only for high rssi) */
+       {  7,  0  }, /* lvl 7 (only for high rssi) */
+       {  8,  0  }  /* lvl 8 (only for high rssi) */
 };
 
 #define ATH9K_ANI_CCK_NUM_LEVEL \
@@ -177,10 +177,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
            BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
                weak_sig = true;
 
-       if (aniState->ofdmWeakSigDetect != weak_sig)
-                       ath9k_hw_ani_control(ah,
-                               ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
-                               entry_ofdm->ofdm_weak_signal_on);
+       /*
+        * OFDM Weak signal detection is always enabled for AP mode.
+        */
+       if (ah->opmode != NL80211_IFTYPE_AP &&
+           aniState->ofdmWeakSigDetect != weak_sig) {
+               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    entry_ofdm->ofdm_weak_signal_on);
+       }
 
        if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
                ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
@@ -363,18 +368,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
        ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);
        ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);
 
-       /*
-        * enable phy counters if hw supports or if not, enable phy
-        * interrupts (so we can count each one)
-        */
        ath9k_ani_restart(ah);
-
-       ENABLE_REGWRITE_BUFFER(ah);
-
-       REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
-       REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-
-       REGWRITE_BUFFER_FLUSH(ah);
 }
 
 static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
index 78b9fa9..b54a3fb 100644 (file)
 #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
 
 /* units are errors per second */
-#define ATH9K_ANI_OFDM_TRIG_HIGH          3500
+#define ATH9K_ANI_OFDM_TRIG_HIGH           3500
 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
 
-/* units are errors per second */
 #define ATH9K_ANI_OFDM_TRIG_LOW           400
 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
 
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_HIGH           600
-
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_LOW            300
 
-#define ATH9K_ANI_NOISE_IMMUNE_LVL        4
 #define ATH9K_ANI_SPUR_IMMUNE_LVL         3
 #define ATH9K_ANI_FIRSTEP_LVL             2
 
 /* in ms */
 #define ATH9K_ANI_POLLINTERVAL            1000
 
-#define HAL_NOISE_IMMUNE_MAX              4
-#define HAL_SPUR_IMMUNE_MAX               7
-#define HAL_FIRST_STEP_MAX                2
-
 #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
 #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN    0
index e6b92ff..1e86977 100644 (file)
@@ -3563,14 +3563,24 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        int chain;
-       u32 regval;
+       u32 regval, value, gpio;
        static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = {
                        AR_PHY_SWITCH_CHAIN_0,
                        AR_PHY_SWITCH_CHAIN_1,
                        AR_PHY_SWITCH_CHAIN_2,
        };
 
-       u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
+       if (AR_SREV_9485(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) {
+               if (ah->config.xlna_gpio)
+                       gpio = ah->config.xlna_gpio;
+               else
+                       gpio = AR9300_EXT_LNA_CTL_GPIO_AR9485;
+
+               ath9k_hw_cfg_output(ah, gpio,
+                                   AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED);
+       }
+
+       value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
 
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
@@ -3796,7 +3806,13 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value);
 
-                       value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+                       if (AR_SREV_9485(ah) &&
+                           (ar9003_hw_get_rx_gain_idx(ah) == 0) &&
+                           ah->config.xatten_margin_cfg)
+                               value = 5;
+                       else
+                               value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
                                      value);
@@ -4546,7 +4562,7 @@ static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah,
                                                 is2GHz);
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 }
@@ -5272,7 +5288,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
                return;
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 
index a3523c9..671aaa7 100644 (file)
@@ -627,9 +627,26 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
 
 static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
 {
-       if (AR_SREV_9462_20(ah))
+       if (AR_SREV_9462_20(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9462_common_mixed_rx_gain_table_2p0);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                              ar9462_2p0_baseband_core_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                              ar9462_2p0_baseband_postamble_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
+}
+
+static void ar9003_rx_gain_table_mode3(struct ath_hw *ah)
+{
+       if (AR_SREV_9462_20(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p0_5g_xlna_only_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
 }
 
 static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
@@ -645,6 +662,9 @@ static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
        case 2:
                ar9003_rx_gain_table_mode2(ah);
                break;
+       case 3:
+               ar9003_rx_gain_table_mode3(ah);
+               break;
        }
 }
 
index 83e0385..df84d20 100644 (file)
@@ -735,6 +735,9 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                return -EINVAL;
        }
 
+       /*
+        * SOC, MAC, BB, RADIO initvals.
+        */
        for (i = 0; i < ATH_INI_NUM_SPLIT; i++) {
                ar9003_hw_prog_ini(ah, &ah->iniSOC[i], modesIndex);
                ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex);
@@ -746,11 +749,39 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                                           modesIndex);
        }
 
+       /*
+        * RXGAIN initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites);
+
+       if (AR_SREV_9462_20(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);
+               }
+
+               /*
+                * 5G-XLNA
+                */
+               if ((ar9003_hw_get_rx_gain_idx(ah) == 2) ||
+                   (ar9003_hw_get_rx_gain_idx(ah) == 3)) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                                       modesIndex, regWrites);
+               }
+       }
+
        if (AR_SREV_9550(ah))
                REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex,
                                regWrites);
 
+       /*
+        * TXGAIN initvals.
+        */
        if (AR_SREV_9550(ah)) {
                int modes_txgain_index;
 
@@ -772,8 +803,14 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                REG_WRITE_ARRAY(&ah->iniModesFastClock,
                                modesIndex, regWrites);
 
+       /*
+        * Clock frequency initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
 
+       /*
+        * JAPAN regulatory.
+        */
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
@@ -906,6 +943,11 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_channel *chan = ah->curchan;
        struct ar5416AniState *aniState = &ah->ani;
+       int m1ThreshLow, m2ThreshLow;
+       int m1Thresh, m2Thresh;
+       int m2CountThr, m2CountThrLow;
+       int m1ThreshLowExt, m2ThreshLowExt;
+       int m1ThreshExt, m2ThreshExt;
        s32 value, value2;
 
        switch (cmd & ah->ani_function) {
@@ -919,6 +961,61 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
                 */
                u32 on = param ? 1 : 0;
 
+               if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+                       goto skip_ws_det;
+
+               m1ThreshLow = on ?
+                       aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+               m2ThreshLow = on ?
+                       aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+               m1Thresh = on ?
+                       aniState->iniDef.m1Thresh : m1Thresh_off;
+               m2Thresh = on ?
+                       aniState->iniDef.m2Thresh : m2Thresh_off;
+               m2CountThr = on ?
+                       aniState->iniDef.m2CountThr : m2CountThr_off;
+               m2CountThrLow = on ?
+                       aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+               m1ThreshLowExt = on ?
+                       aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+               m2ThreshLowExt = on ?
+                       aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+               m1ThreshExt = on ?
+                       aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+               m2ThreshExt = on ?
+                       aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+                             m1ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+                             m2ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M1_THRESH,
+                             m1Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2_THRESH,
+                             m2Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2COUNT_THR,
+                             m2CountThr);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+                             m2CountThrLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+                             m1ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+                             m2ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH,
+                             m1ThreshExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH,
+                             m2ThreshExt);
+skip_ws_det:
                if (on)
                        REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
                                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
index e717741..5013c73 100644 (file)
 
 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ          -118
 
+#define AR9300_EXT_LNA_CTL_GPIO_AR9485 9
+
 /*
  * AGC Field Definitions
  */
index 999ab08..1d6b705 100644 (file)
@@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
        {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
        {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
        {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
-       {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
        {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
        {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -1449,4 +1449,284 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = {
        {0x0000b1fc, 0x00000196},
 };
 
+static const u32 ar9462_2p0_baseband_postamble_5g_xlna[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
+};
+
+static const u32 ar9462_2p0_5g_xlna_only_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p0_baseband_core_mix_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x00009fd0, 0x0a2d6b93},
+};
+
+static const u32 ar9462_2p0_baseband_postamble_mix_rxgain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da},
+       {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8},
+       {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e},
+};
+
 #endif /* INITVALS_9462_2P0_H */
index 18fcee4..04b2d3e 100644 (file)
@@ -296,6 +296,7 @@ struct ath_tx {
        struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
        struct ath_descdma txdma;
        struct ath_txq *txq_map[IEEE80211_NUM_ACS];
+       struct ath_txq *uapsdq;
        u32 txq_max_pending[IEEE80211_NUM_ACS];
        u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
 };
@@ -343,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
 int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 struct ath_tx_control *txctl);
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb);
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -353,6 +356,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
                       struct ath_node *an);
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data);
 
 /********/
 /* VIFs */
@@ -623,6 +631,10 @@ 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
+
 /*
  * Default cache line size, in bytes.
  * Used when PCI device not fully initialized by bootrom/BIOS
@@ -707,6 +719,7 @@ struct ath_softc {
 
        unsigned int hw_busy_count;
        unsigned long sc_flags;
+       unsigned long driver_data;
 
        u32 intrstatus;
        u16 ps_flags; /* PS_* */
index fd1eeba..1a17732 100644 (file)
@@ -108,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
-static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
-       struct ath_softc *sc = hw->priv;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_tx_control txctl;
-
-       memset(&txctl, 0, sizeof(struct ath_tx_control));
-       txctl.txq = sc->beacon.cabq;
-
-       ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb);
-
-       if (ath_tx_start(hw, skb, &txctl) != 0) {
-               ath_dbg(common, XMIT, "CABQ TX failed\n");
-               ieee80211_free_txskb(hw, skb);
-       }
-}
-
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif)
 {
@@ -206,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 
        ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
 
-       while (skb) {
-               ath9k_tx_cabq(hw, skb);
-               skb = ieee80211_get_buffered_bc(hw, vif);
-       }
+       if (skb)
+               ath_tx_cabq(hw, vif, skb);
 
        return bf;
 }
index 7304e75..5e8219a 100644 (file)
@@ -387,7 +387,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 
        if (!caldata) {
                chan->noisefloor = nf;
-               ah->noise = ath9k_hw_getchan_noise(ah, chan);
                return false;
        }
 
index 0085e64..6958103 100644 (file)
@@ -142,6 +142,7 @@ struct ath9k_htc_target_aggr {
 #define WLAN_RC_40_FLAG  0x02
 #define WLAN_RC_SGI_FLAG 0x04
 #define WLAN_RC_HT_FLAG  0x08
+#define ATH_RC_TX_STBC_FLAG 0x20
 
 struct ath9k_htc_rateset {
        u8 rs_nrates;
index 59f6436..bb0ba9e 100644 (file)
@@ -517,6 +517,9 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv,
        ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
                tx_streams, rx_streams);
 
+       if (tx_streams >= 2)
+               ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+
        if (tx_streams != rx_streams) {
                ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
                ht_info->mcs.tx_params |= ((tx_streams - 1) <<
index 34869c2..eaa94fe 100644 (file)
@@ -627,6 +627,8 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
                trate->rates.ht_rates.rs_nrates = j;
 
                caps = WLAN_RC_HT_FLAG;
+               if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+                       caps |= ATH_RC_TX_STBC_FLAG;
                if (sta->ht_cap.mcs.rx_mask[1])
                        caps |= WLAN_RC_DS_FLAG;
                if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
index d813ab8..5324c33 100644 (file)
@@ -1870,7 +1870,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ah->caldata = caldata;
        if (caldata && (chan->channel != caldata->channel ||
-                       chan->channelFlags != caldata->channelFlags)) {
+                       chan->channelFlags != caldata->channelFlags ||
+                       chan->chanmode != caldata->chanmode)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
@@ -3041,7 +3042,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
 
        timer_next = tsf + trig_timeout;
 
-       ath_dbg(ath9k_hw_common(ah), HWTIMER,
+       ath_dbg(ath9k_hw_common(ah), BTCOEX,
                "current tsf %x period %x timer_next %x\n",
                tsf, timer_period, timer_next);
 
@@ -3140,7 +3141,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &thresh_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n",
+               ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n",
                        index);
                timer->overflow(timer->arg);
        }
@@ -3149,7 +3150,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &trigger_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER,
+               ath_dbg(common, BTCOEX,
                        "Gen timer[%d] trigger\n", index);
                timer->trigger(timer->arg);
        }
index 7d259b7..cd74b3a 100644 (file)
@@ -307,6 +307,10 @@ struct ath9k_ops_config {
        u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
        u8 max_txtrig_level;
        u16 ani_poll_interval; /* ANI poll interval in ms */
+
+       /* Platform specific config */
+       u32 xlna_gpio;
+       bool xatten_margin_cfg;
 };
 
 enum ath9k_int {
@@ -888,6 +892,9 @@ struct ath_hw {
        struct ar5416IniArray iniCckfirJapan2484;
        struct ar5416IniArray iniModes_9271_ANI_reg;
        struct ar5416IniArray ini_radio_post_sys2ant;
+       struct ar5416IniArray ini_modes_rxgain_5g_xlna;
+       struct ar5416IniArray ini_modes_rxgain_bb_core;
+       struct ar5416IniArray ini_modes_rxgain_bb_postamble;
 
        struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
        struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
index 389ee1b..1e555d8 100644 (file)
@@ -432,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc)
        sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
        ath_cabq_update(sc);
 
+       sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
                sc->tx.txq_map[i]->mac80211_qnum = i;
@@ -511,6 +513,27 @@ static void ath9k_init_misc(struct ath_softc *sc)
        sc->spec_config.fft_period = 0xF;
 }
 
+static void ath9k_init_platform(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (common->bus_ops->ath_bus_type != ATH_PCI)
+               return;
+
+       if (sc->driver_data & (ATH9K_PCI_CUS198 |
+                              ATH9K_PCI_CUS230)) {
+               ah->config.xlna_gpio = 9;
+               ah->config.xatten_margin_cfg = true;
+
+               ath_info(common, "Set parameters for %s\n",
+                        (sc->driver_data & ATH9K_PCI_CUS198) ?
+                        "CUS198" : "CUS230");
+       } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+               ath_info(common, "CUS217 card detected\n");
+       }
+}
+
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
                                    void *ctx)
 {
@@ -602,6 +625,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        common->btcoex_enabled = ath9k_btcoex_enable == 1;
        common->disable_ani = false;
 
+       /*
+        * Platform quirks.
+        */
+       ath9k_init_platform(sc);
+
        /*
         * Enable Antenna diversity only when BTCOEX is disabled
         * and the user manually requests the feature.
@@ -753,6 +781,15 @@ static const struct ieee80211_iface_combination if_comb[] = {
        }
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath9k_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+       .n_patterns = MAX_NUM_USER_PATTERN,
+       .pattern_min_len = 1,
+       .pattern_max_len = MAX_PATTERN_SIZE,
+};
+#endif
+
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -800,13 +837,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 
 #ifdef CONFIG_PM_SLEEP
        if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
-           device_can_wakeup(sc->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT;
-               hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN;
-               hw->wiphy->wowlan.pattern_min_len = 1;
-               hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE;
-       }
+           device_can_wakeup(sc->dev))
+               hw->wiphy->wowlan = &ath9k_wowlan_support;
 
        atomic_set(&sc->wow_sleep_proc_intr, -1);
        atomic_set(&sc->wow_got_bmiss_intr, -1);
index e5b186b..1737a3e 100644 (file)
@@ -1210,13 +1210,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                ath_update_survey_stats(sc);
                spin_unlock_irqrestore(&common->cc_lock, flags);
 
-               /*
-                * Preserve the current channel values, before updating
-                * the same channel
-                */
-               if (ah->curchan && (old_pos == pos))
-                       ath9k_hw_getnf(ah, ah->curchan);
-
                ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
                                          curchan, channel_type);
 
@@ -2347,6 +2340,7 @@ struct ieee80211_ops ath9k_ops = {
        .flush              = ath9k_flush,
        .tx_frames_pending  = ath9k_tx_frames_pending,
        .tx_last_beacon     = ath9k_tx_last_beacon,
+       .release_buffered_frames = ath9k_release_buffered_frames,
        .get_stats          = ath9k_get_stats,
        .set_antenna        = ath9k_set_antenna,
        .get_antenna        = ath9k_get_antenna,
index 0e0d395..b096bb2 100644 (file)
@@ -34,8 +34,51 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */
        { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E  AR9300 */
+
+       /* PCI-E CUS198 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2086),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1237),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2126),
+         .driver_data = ATH9K_PCI_CUS198 },
+
+       /* PCI-E CUS230 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2152),
+         .driver_data = ATH9K_PCI_CUS230 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE075),
+         .driver_data = ATH9K_PCI_CUS230 },
+
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
+
+       /* PCI-E CUS217 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2116),
+         .driver_data = ATH9K_PCI_CUS217 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x11AD, /* LITEON */
+                        0x6661),
+         .driver_data = ATH9K_PCI_CUS217 },
+
        { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E  AR9462 */
        { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E  AR1111/AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E  AR9565 */
@@ -221,6 +264,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sc->hw = hw;
        sc->dev = &pdev->dev;
        sc->mem = pcim_iomap_table(pdev)[0];
+       sc->driver_data = id->driver_data;
 
        /* Will be cleared in ath9k_start() */
        set_bit(SC_OP_INVALID, &sc->sc_flags);
index 1c9b1ba..7e19d9b 100644 (file)
@@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
                                !txfail);
                } else {
+                       if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) {
+                               tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+                               ieee80211_sta_eosp(sta);
+                       }
                        /* retry the un-acked ones */
                        if (bf->bf_next == NULL && bf_last->bf_stale) {
                                struct ath_buf *tbf;
@@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        return ndelim;
 }
 
-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 struct ath_buf *
+ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
+                       struct ath_atx_tid *tid)
 {
-#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;
-       u16 aggr_limit = 0, al = 0, bpad = 0,
-               al_delta, h_baw = tid->baw_size / 2;
-       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
-       struct ieee80211_tx_info *tx_info;
        struct ath_frame_info *fi;
        struct sk_buff *skb;
+       struct ath_buf *bf;
        u16 seqno;
 
-       do {
+       while (1) {
                skb = skb_peek(&tid->buf_q);
+               if (!skb)
+                       break;
+
                fi = get_frame_info(skb);
                bf = fi->bf;
                if (!fi->bf)
@@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                seqno = bf->bf_state.seqno;
 
                /* do not step over block-ack window */
-               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
-                       status = ATH_AGGR_BAW_CLOSED;
+               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
                        break;
-               }
 
                if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
                        struct ath_tx_status ts = {};
@@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        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)
+{
+#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;
+       u16 aggr_limit = 0, al = 0, bpad = 0,
+               al_delta, h_baw = tid->baw_size / 2;
+       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+       struct ieee80211_tx_info *tx_info;
+       struct ath_frame_info *fi;
+       struct sk_buff *skb;
+
+       do {
+               bf = ath_tx_get_tid_subframe(sc, txq, tid);
+               if (!bf) {
+                       status = ATH_AGGR_BAW_CLOSED;
+                       break;
+               }
+
+               skb = bf->bf_mpdu;
+               fi = get_frame_info(skb);
+
                if (!bf_first)
                        bf_first = bf;
 
@@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 
                /* link buffers of this frame to the aggregate */
                if (!fi->retries)
-                       ath_tx_addto_baw(sc, tid, seqno);
+                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
                bf->bf_state.ndelim = ndelim;
 
                __skb_unlink(skb, &tid->buf_q);
@@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                             struct ath_txq *txq, int len)
 {
        struct ath_hw *ah = sc->sc_ah;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-       struct ath_buf *bf_first = bf;
+       struct ath_buf *bf_first = NULL;
        struct ath_tx_info info;
-       bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
        memset(&info, 0, sizeof(info));
        info.is_first = true;
@@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        info.txpower = MAX_RATE_POWER;
        info.qcu = txq->axq_qnum;
 
-       info.flags = ATH9K_TXDESC_INTREQ;
-       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
-               info.flags |= ATH9K_TXDESC_NOACK;
-       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
-               info.flags |= ATH9K_TXDESC_LDPC;
-
-       ath_buf_set_rate(sc, bf, &info, len);
-
-       if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-               info.flags |= ATH9K_TXDESC_CLRDMASK;
-
-       if (bf->bf_state.bfs_paprd)
-               info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S;
-
-
        while (bf) {
                struct sk_buff *skb = bf->bf_mpdu;
+               struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
                struct ath_frame_info *fi = get_frame_info(skb);
+               bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
                info.type = get_hw_packet_type(skb);
                if (bf->bf_next)
@@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                else
                        info.link = 0;
 
+               if (!bf_first) {
+                       bf_first = bf;
+
+                       info.flags = ATH9K_TXDESC_INTREQ;
+                       if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
+                           txq == sc->tx.uapsdq)
+                               info.flags |= ATH9K_TXDESC_CLRDMASK;
+
+                       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+                               info.flags |= ATH9K_TXDESC_NOACK;
+                       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
+                               info.flags |= ATH9K_TXDESC_LDPC;
+
+                       if (bf->bf_state.bfs_paprd)
+                               info.flags |= (u32) bf->bf_state.bfs_paprd <<
+                                             ATH9K_TXDESC_PAPRD_S;
+
+                       ath_buf_set_rate(sc, bf, &info, len);
+               }
+
                info.buf_addr[0] = bf->bf_buf_addr;
                info.buf_len[0] = skb->len;
                info.pkt_len = fi->framelen;
@@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                if (aggr) {
                        if (bf == bf_first)
                                info.aggr = AGGR_BUF_FIRST;
-                       else if (!bf->bf_next)
+                       else if (bf == bf_first->bf_lastbf)
                                info.aggr = AGGR_BUF_LAST;
                        else
                                info.aggr = AGGR_BUF_MIDDLE;
@@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                        info.aggr_len = len;
                }
 
+               if (bf == bf_first->bf_lastbf)
+                       bf_first = NULL;
+
                ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
                bf = bf->bf_next;
        }
@@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        ath_txq_unlock_complete(sc, txq);
 }
 
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_node *an = (struct ath_node *)sta->drv_priv;
+       struct ath_txq *txq = sc->tx.uapsdq;
+       struct ieee80211_tx_info *info;
+       struct list_head bf_q;
+       struct ath_buf *bf_tail = NULL, *bf;
+       int sent = 0;
+       int i;
+
+       INIT_LIST_HEAD(&bf_q);
+       for (i = 0; tids && nframes; i++, tids >>= 1) {
+               struct ath_atx_tid *tid;
+
+               if (!(tids & 1))
+                       continue;
+
+               tid = ATH_AN_2_TID(an, i);
+               if (tid->paused)
+                       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);
+                       if (!bf)
+                               break;
+
+                       __skb_unlink(bf->bf_mpdu, &tid->buf_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);
+                       bf->bf_state.bf_type &= ~BUF_AGGR;
+                       if (bf_tail)
+                               bf_tail->bf_next = bf;
+
+                       bf_tail = bf;
+                       nframes--;
+                       sent++;
+                       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
+
+                       if (skb_queue_empty(&tid->buf_q))
+                               ieee80211_sta_set_buffered(an->sta, i, false);
+               }
+               ath_txq_unlock_complete(sc, tid->ac->txq);
+       }
+
+       if (list_empty(&bf_q))
+               return;
+
+       info = IEEE80211_SKB_CB(bf_tail->bf_mpdu);
+       info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       ath_txq_lock(sc, txq);
+       ath_tx_fill_desc(sc, bf, txq, 0);
+       ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+       ath_txq_unlock(sc, txq);
+}
+
 /********************/
 /* Queue Management */
 /********************/
@@ -1681,8 +1784,9 @@ 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_atx_tid *tid,
-                             struct sk_buff *skb, struct ath_tx_control *txctl)
+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;
@@ -1695,21 +1799,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
         * - 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) ||
-           txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) {
+       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(txctl->txq->axq_qnum, a_queued_sw);
+               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(txctl->txq, tid);
+                       ath_tx_queue_tid(txq, tid);
                return;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                ieee80211_free_txskb(sc->hw, skb);
                return;
@@ -1724,10 +1829,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
        ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
 
        /* Queue to h/w without aggregation */
-       TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
+       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
        bf->bf_lastbf = bf;
-       ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen);
-       ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false);
+       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,
@@ -1865,22 +1970,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 }
 
-/* Upon failure caller should free skb */
-int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
-                struct ath_tx_control *txctl)
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+                         struct ath_tx_control *txctl)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
        struct ath_softc *sc = hw->priv;
-       struct ath_txq *txq = txctl->txq;
-       struct ath_atx_tid *tid = NULL;
-       struct ath_buf *bf;
-       int padpos, padsize;
        int frmlen = skb->len + FCS_LEN;
-       u8 tidno;
-       int q;
+       int padpos, padsize;
 
        /* NOTE:  sta can be NULL according to net/mac80211.h */
        if (sta)
@@ -1901,6 +2000,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if ((vif && vif->type != NL80211_IFTYPE_AP &&
+                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
+           !ieee80211_is_data(hdr->frame_control))
+               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
        /* Add the padding after the header if this is not already done */
        padpos = ieee80211_hdrlen(hdr->frame_control);
        padsize = padpos & 3;
@@ -1910,16 +2014,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 
                skb_push(skb, padsize);
                memmove(skb->data, skb->data + padsize, padpos);
-               hdr = (struct ieee80211_hdr *) skb->data;
        }
 
-       if ((vif && vif->type != NL80211_IFTYPE_AP &&
-                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
-           !ieee80211_is_data(hdr->frame_control))
-               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-
        setup_frame_info(hw, sta, skb, frmlen);
+       return 0;
+}
 
+
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+                struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = txctl->sta;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath_softc *sc = hw->priv;
+       struct ath_txq *txq = txctl->txq;
+       struct ath_atx_tid *tid = NULL;
+       struct ath_buf *bf;
+       u8 tidno;
+       int q;
+       int ret;
+
+       ret = ath_tx_prepare(hw, skb, txctl);
+       if (ret)
+           return ret;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
        /*
         * At this point, the vif, hw_key and sta pointers in the tx control
         * info are no longer valid (overwritten by the ath_frame_info data.
@@ -1935,6 +2057,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                txq->stopped = true;
        }
 
+       if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+               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;
@@ -1948,11 +2076,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 * Try aggregation if it's a unicast data frame
                 * and the destination is HT capable.
                 */
-               ath_tx_send_ampdu(sc, tid, skb, txctl);
+               ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
                goto out;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                if (txctl->paprd)
                        dev_kfree_skb_any(skb);
@@ -1967,7 +2095,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                bf->bf_state.bfs_paprd_timestamp = jiffies;
 
        ath_set_rates(vif, sta, bf);
-       ath_tx_send_normal(sc, txctl->txq, tid, skb);
+       ath_tx_send_normal(sc, txq, tid, skb);
 
 out:
        ath_txq_unlock(sc, txq);
@@ -1975,6 +2103,74 @@ out:
        return 0;
 }
 
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_tx_control txctl = {
+               .txq = sc->beacon.cabq
+       };
+       struct ath_tx_info info = {};
+       struct ieee80211_hdr *hdr;
+       struct ath_buf *bf_tail = NULL;
+       struct ath_buf *bf;
+       LIST_HEAD(bf_q);
+       int duration = 0;
+       int max_duration;
+
+       max_duration =
+               sc->cur_beacon_conf.beacon_interval * 1000 *
+               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+
+       do {
+               struct ath_frame_info *fi = get_frame_info(skb);
+
+               if (ath_tx_prepare(hw, skb, &txctl))
+                       break;
+
+               bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb);
+               if (!bf)
+                       break;
+
+               bf->bf_lastbf = bf;
+               ath_set_rates(vif, NULL, bf);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               duration += info.rates[0].PktDuration;
+               if (bf_tail)
+                       bf_tail->bf_next = bf;
+
+               list_add_tail(&bf->list, &bf_q);
+               bf_tail = bf;
+               skb = NULL;
+
+               if (duration > max_duration)
+                       break;
+
+               skb = ieee80211_get_buffered_bc(hw, vif);
+       } while(skb);
+
+       if (skb)
+               ieee80211_free_txskb(hw, skb);
+
+       if (list_empty(&bf_q))
+               return;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
+
+       if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) {
+               hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA;
+               dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+                       sizeof(*hdr), DMA_TO_DEVICE);
+       }
+
+       ath_txq_lock(sc, txctl.txq);
+       ath_tx_fill_desc(sc, bf, txctl.txq, 0);
+       ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false);
+       TX_STAT_INC(txctl.txq->axq_qnum, queued);
+       ath_txq_unlock(sc, txctl.txq);
+}
+
 /*****************/
 /* TX Completion */
 /*****************/
@@ -2020,7 +2216,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        }
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+       __skb_queue_tail(&txq->complete_q, skb);
+
        q = skb_get_queue_mapping(skb);
+       if (txq == sc->tx.uapsdq)
+               txq = sc->tx.txq_map[q];
+
        if (txq == sc->tx.txq_map[q]) {
                if (WARN_ON(--txq->pending_frames < 0))
                        txq->pending_frames = 0;
@@ -2031,8 +2232,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                        txq->stopped = false;
                }
        }
-
-       __skb_queue_tail(&txq->complete_q, skb);
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
index 9dce106..8596aba 100644 (file)
@@ -133,6 +133,9 @@ struct carl9170_sta_tid {
 
        /* Preaggregation reorder queue */
        struct sk_buff_head queue;
+
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif;
 };
 
 #define CARL9170_QUEUE_TIMEOUT         256
index e9010a4..4a33c6e 100644 (file)
@@ -1448,6 +1448,8 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                tid_info->state = CARL9170_TID_STATE_PROGRESS;
                tid_info->tid = tid;
                tid_info->max = sta_info->ampdu_max_len;
+               tid_info->sta = sta;
+               tid_info->vif = vif;
 
                INIT_LIST_HEAD(&tid_info->list);
                INIT_LIST_HEAD(&tid_info->tmp_list);
@@ -1857,6 +1859,7 @@ void *carl9170_alloc(size_t priv_size)
                     IEEE80211_HW_SUPPORTS_PS |
                     IEEE80211_HW_PS_NULLFUNC_STACK |
                     IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
+                    IEEE80211_HW_SUPPORTS_RC_TABLE |
                     IEEE80211_HW_SIGNAL_DBM;
 
        if (!modparam_noht) {
index c61cafa..e3f696e 100644 (file)
@@ -625,7 +625,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
                    msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
                        goto unlock;
 
-               sta = __carl9170_get_tx_sta(ar, skb);
+               sta = iter->sta;
                if (WARN_ON(!sta))
                        goto unlock;
 
@@ -866,6 +866,93 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar,
        return false;
 }
 
+static void carl9170_tx_get_rates(struct ar9170 *ar,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta,
+                                 struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info;
+
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES > IEEE80211_TX_RATE_TABLE_SIZE);
+
+       info = IEEE80211_SKB_CB(skb);
+
+       ieee80211_get_tx_rates(vif, sta, skb,
+                              info->control.rates,
+                              IEEE80211_TX_MAX_RATES);
+}
+
+static void carl9170_tx_apply_rateset(struct ar9170 *ar,
+                                     struct ieee80211_tx_info *sinfo,
+                                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_rate *txrate;
+       struct ieee80211_tx_info *info;
+       struct _carl9170_tx_superframe *txc = (void *) skb->data;
+       int i;
+       bool ampdu;
+       bool no_ack;
+
+       info = IEEE80211_SKB_CB(skb);
+       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
+       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+       /* Set the rate control probe flag for all (sub-) frames.
+        * This is because the TX_STATS_AMPDU flag is only set on
+        * the last frame, so it has to be inherited.
+        */
+       info->flags |= (sinfo->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+
+       /* NOTE: For the first rate, the ERP & AMPDU flags are directly
+        * taken from mac_control. For all fallback rate, the firmware
+        * updates the mac_control flags from the rate info field.
+        */
+       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
+               __le32 phy_set;
+
+               txrate = &sinfo->control.rates[i];
+               if (txrate->idx < 0)
+                       break;
+
+               phy_set = carl9170_tx_physet(ar, info, txrate);
+               if (i == 0) {
+                       __le16 mac_tmp = cpu_to_le16(0);
+
+                       /* first rate - part of the hw's frame header */
+                       txc->f.phy_control = phy_set;
+
+                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+
+                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+                       else if (carl9170_tx_cts_check(ar, txrate))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+                       txc->f.mac_control |= mac_tmp;
+               } else {
+                       /* fallback rates are stored in the firmware's
+                        * retry rate set array.
+                        */
+                       txc->s.rr[i - 1] = phy_set;
+               }
+
+               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
+                       txrate->count);
+
+               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+               else if (carl9170_tx_cts_check(ar, txrate))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+
+               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
+                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
+       }
+}
+
 static int carl9170_tx_prepare(struct ar9170 *ar,
                               struct ieee80211_sta *sta,
                               struct sk_buff *skb)
@@ -874,13 +961,10 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        struct _carl9170_tx_superframe *txc;
        struct carl9170_vif_info *cvif;
        struct ieee80211_tx_info *info;
-       struct ieee80211_tx_rate *txrate;
        struct carl9170_tx_info *arinfo;
        unsigned int hw_queue;
-       int i;
        __le16 mac_tmp;
        u16 len;
-       bool ampdu, no_ack;
 
        BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
        BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) !=
@@ -889,8 +973,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) !=
                     AR9170_TX_HWDESC_LEN);
 
-       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
-
        BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC >
                ((CARL9170_TX_SUPER_MISC_VIF_ID >>
                 CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1));
@@ -932,8 +1014,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) &
                               AR9170_TX_MAC_QOS);
 
-       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
-       if (unlikely(no_ack))
+       if (unlikely(info->flags & IEEE80211_TX_CTL_NO_ACK))
                mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
 
        if (info->control.hw_key) {
@@ -954,8 +1035,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                }
        }
 
-       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
-       if (ampdu) {
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
                unsigned int density, factor;
 
                if (unlikely(!sta || !cvif))
@@ -982,50 +1062,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                        txc->s.ampdu_settings, factor);
        }
 
-       /*
-        * NOTE: For the first rate, the ERP & AMPDU flags are directly
-        * taken from mac_control. For all fallback rate, the firmware
-        * updates the mac_control flags from the rate info field.
-        */
-       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
-               __le32 phy_set;
-               txrate = &info->control.rates[i];
-               if (txrate->idx < 0)
-                       break;
-
-               phy_set = carl9170_tx_physet(ar, info, txrate);
-               if (i == 0) {
-                       /* first rate - part of the hw's frame header */
-                       txc->f.phy_control = phy_set;
-
-                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
-                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
-                       else if (carl9170_tx_cts_check(ar, txrate))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
-
-               } else {
-                       /* fallback rates are stored in the firmware's
-                        * retry rate set array.
-                        */
-                       txc->s.rr[i - 1] = phy_set;
-               }
-
-               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
-                       txrate->count);
-
-               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-               else if (carl9170_tx_cts_check(ar, txrate))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-
-               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
-                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
-       }
-
        txc->s.len = cpu_to_le16(skb->len);
        txc->f.length = cpu_to_le16(len + FCS_LEN);
        txc->f.mac_control = mac_tmp;
@@ -1086,31 +1122,12 @@ static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb)
        }
 }
 
-static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest,
-                                  struct sk_buff *_src)
-{
-       struct _carl9170_tx_superframe *dest, *src;
-
-       dest = (void *) _dest->data;
-       src = (void *) _src->data;
-
-       /*
-        * The mac80211 rate control algorithm expects that all MPDUs in
-        * an AMPDU share the same tx vectors.
-        * This is not really obvious right now, because the hardware
-        * does the AMPDU setup according to its own rulebook.
-        * Our nicely assembled, strictly monotonic increasing mpdu
-        * chains will be broken up, mashed back together...
-        */
-
-       return (dest->f.phy_control == src->f.phy_control);
-}
-
 static void carl9170_tx_ampdu(struct ar9170 *ar)
 {
        struct sk_buff_head agg;
        struct carl9170_sta_tid *tid_info;
        struct sk_buff *skb, *first;
+       struct ieee80211_tx_info *tx_info_first;
        unsigned int i = 0, done_ampdus = 0;
        u16 seq, queue, tmpssn;
 
@@ -1156,6 +1173,7 @@ retry:
                        goto processed;
                }
 
+               tx_info_first = NULL;
                while ((skb = skb_peek(&tid_info->queue))) {
                        /* strict 0, 1, ..., n - 1, n frame sequence order */
                        if (unlikely(carl9170_get_seq(skb) != seq))
@@ -1166,8 +1184,13 @@ retry:
                            (tid_info->max - 1)))
                                break;
 
-                       if (!carl9170_tx_rate_check(ar, skb, first))
-                               break;
+                       if (!tx_info_first) {
+                               carl9170_tx_get_rates(ar, tid_info->vif,
+                                                     tid_info->sta, first);
+                               tx_info_first = IEEE80211_SKB_CB(first);
+                       }
+
+                       carl9170_tx_apply_rateset(ar, tx_info_first, skb);
 
                        atomic_inc(&ar->tx_ampdu_upload);
                        tid_info->snx = seq = SEQ_NEXT(seq);
@@ -1182,8 +1205,7 @@ retry:
                if (skb_queue_empty(&tid_info->queue) ||
                    carl9170_get_seq(skb_peek(&tid_info->queue)) !=
                    tid_info->snx) {
-                       /*
-                        * stop TID, if A-MPDU frames are still missing,
+                       /* stop TID, if A-MPDU frames are still missing,
                         * or whenever the queue is empty.
                         */
 
@@ -1450,12 +1472,14 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        struct ar9170 *ar = hw->priv;
        struct ieee80211_tx_info *info;
        struct ieee80211_sta *sta = control->sta;
+       struct ieee80211_vif *vif;
        bool run;
 
        if (unlikely(!IS_STARTED(ar)))
                goto err_free;
 
        info = IEEE80211_SKB_CB(skb);
+       vif = info->control.vif;
 
        if (unlikely(carl9170_tx_prepare(ar, sta, skb)))
                goto err_free;
@@ -1486,6 +1510,8 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        } else {
                unsigned int queue = skb_get_queue_mapping(skb);
 
+               carl9170_tx_get_rates(ar, vif, sta, skb);
+               carl9170_tx_apply_rateset(ar, info, skb);
                skb_queue_tail(&ar->tx_pending[queue], skb);
        }
 
index ccc4c71..7d077c7 100644 (file)
@@ -42,11 +42,11 @@ static int __ath_regd_init(struct ath_regulatory *reg);
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
 
 /* We allow IBSS on these on a case by case basis by regulatory domain */
-#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
 
 #define ATH9K_2GHZ_ALL         ATH9K_2GHZ_CH01_11, \
index 5644ac5..ce8c038 100644 (file)
@@ -28,7 +28,7 @@ config WIL6210_ISR_COR
          such monitoring impossible.
          Say y unless you debug interrupts
 
-config ATH6KL_TRACING
+config WIL6210_TRACING
        bool "wil6210 tracing support"
        depends on WIL6210
        depends on EVENT_TRACING
index 4eb05d0..61c302a 100644 (file)
@@ -402,6 +402,30 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
        return 0;
 }
 
+static int wil_fix_bcon(struct wil6210_priv *wil,
+                       struct cfg80211_beacon_data *bcon)
+{
+       struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
+       size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       int rc = 0;
+
+       if (bcon->probe_resp_len <= hlen)
+               return 0;
+
+       if (!bcon->proberesp_ies) {
+               bcon->proberesp_ies = f->u.probe_resp.variable;
+               bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+       if (!bcon->assocresp_ies) {
+               bcon->assocresp_ies = f->u.probe_resp.variable;
+               bcon->assocresp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+
+       return rc;
+}
+
 static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                                 struct net_device *ndev,
                                 struct cfg80211_ap_settings *info)
@@ -423,10 +447,18 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
 
+       if (wil_fix_bcon(wil, bcon))
+               wil_dbg_misc(wil, "Fixed bcon\n");
+
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
+       /* Rx VRING. */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
        if (rc)
                return rc;
@@ -455,8 +487,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        if (rc)
                return rc;
 
-       /* Rx VRING. After MAC and beacon */
-       rc = wil_rx_init(wil);
 
        netif_carrier_on(ndev);
 
index c97b864..0a2844c 100644 (file)
@@ -286,41 +286,36 @@ static int __wil_up(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
-       struct ieee80211_channel *channel = wdev->preset_chandef.chan;
        int rc;
-       int bi;
-       u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
-       /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
-       wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
+       /* Rx VRING. After MAC and beacon */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
                wil_dbg_misc(wil, "type: STATION\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_AP:
                wil_dbg_misc(wil, "type: AP\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                wil_dbg_misc(wil, "type: P2P_CLIENT\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_GO:
                wil_dbg_misc(wil, "type: P2P_GO\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_MONITOR:
                wil_dbg_misc(wil, "type: Monitor\n");
-               bi = 0;
                ndev->type = ARPHRD_IEEE80211_RADIOTAP;
                /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
                break;
@@ -328,36 +323,9 @@ static int __wil_up(struct wil6210_priv *wil)
                return -EOPNOTSUPP;
        }
 
-       /* Apply profile in the following order: */
-       /* SSID and channel for the AP */
-       switch (wdev->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_P2P_GO:
-               if (wdev->ssid_len == 0) {
-                       wil_err(wil, "SSID not set\n");
-                       return -EINVAL;
-               }
-               rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
-               if (rc)
-                       return rc;
-               break;
-       default:
-               break;
-       }
-
        /* MAC address - pre-requisite for other commands */
        wmi_set_mac_address(wil, ndev->dev_addr);
 
-       /* Set up beaconing if required. */
-       if (bi > 0) {
-               rc = wmi_pcp_start(wil, bi, wmi_nettype,
-                                  (channel ? channel->hw_value : 0));
-               if (rc)
-                       return rc;
-       }
-
-       /* Rx VRING. After MAC and beacon */
-       wil_rx_init(wil);
 
        napi_enable(&wil->napi_rx);
        napi_enable(&wil->napi_tx);
index 00dffed..e1c492b 100644 (file)
@@ -768,18 +768,16 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                wil_err(wil, "Xmit in monitor mode not supported\n");
                goto drop;
        }
-       if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
-               rc = wmi_tx_eapol(wil, skb);
-       } else {
-               /* find vring */
-               vring = wil_find_tx_vring(wil, skb);
-               if (!vring) {
-                       wil_err(wil, "No Tx VRING available\n");
-                       goto drop;
-               }
-               /* set up vring entry */
-               rc = wil_tx_vring(wil, vring, skb);
+
+       /* find vring */
+       vring = wil_find_tx_vring(wil, skb);
+       if (!vring) {
+               wil_err(wil, "No Tx VRING available\n");
+               goto drop;
        }
+       /* set up vring entry */
+       rc = wil_tx_vring(wil, vring, skb);
+
        switch (rc) {
        case 0:
                /* statistics will be updated on the tx_complete */
index 373cf65..44fdab5 100644 (file)
@@ -329,7 +329,6 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
 int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
 int wmi_set_channel(struct wil6210_priv *wil, int channel);
 int wmi_get_channel(struct wil6210_priv *wil, int *channel);
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr);
 int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
index 527ffb5..dc8059a 100644 (file)
@@ -75,10 +75,11 @@ static const struct {
        {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
        {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
        {0x880000, 0x88a000, 0x880000}, /* various RGF */
-       {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */
+       {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
        /*
         * 920000..930000 ucode code RAM
         * 930000..932000 ucode data RAM
+        * 932000..949000 back-door debug data
         */
 };
 
@@ -314,8 +315,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
                    data->info.channel, data->info.mcs, data->info.snr);
-       wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
-                   le16_to_cpu(data->info.stype));
+       wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
+                   le16_to_cpu(fc));
        wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
                    data->info.qid, data->info.mid, data->info.cid);
 
@@ -739,8 +740,12 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
        if (!wil->secure_pcp)
                cmd.disable_sec = 1;
 
+       /*
+        * Processing time may be huge, in case of secure AP it takes about
+        * 3500ms for FW to start AP
+        */
        rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
-                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100);
+                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
        if (rc)
                return rc;
 
@@ -834,40 +839,6 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
        return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
 }
 
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
-{
-       struct wmi_eapol_tx_cmd *cmd;
-       struct ethhdr *eth;
-       u16 eapol_len = skb->len - ETH_HLEN;
-       void *eapol = skb->data + ETH_HLEN;
-       uint i;
-       int rc;
-
-       skb_set_mac_header(skb, 0);
-       eth = eth_hdr(skb);
-       wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
-       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
-               if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
-                       goto found_dest;
-       }
-
-       return -EINVAL;
-
- found_dest:
-       /* find out eapol data & len */
-       cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL);
-       if (!cmd)
-               return -EINVAL;
-
-       memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN);
-       cmd->eapol_len = cpu_to_le16(eapol_len);
-       memcpy(cmd->eapol, eapol, eapol_len);
-       rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len);
-       kfree(cmd);
-
-       return rc;
-}
-
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr)
 {
index 078e6f3..3f21e0b 100644 (file)
@@ -28,7 +28,7 @@ config B43
 
 config B43_BCMA
        bool "Support for BCMA bus"
-       depends on B43 && BCMA
+       depends on B43 && (BCMA = y || BCMA = B43)
        default y
 
 config B43_BCMA_EXTRA
@@ -39,7 +39,7 @@ config B43_BCMA_EXTRA
 
 config B43_SSB
        bool
-       depends on B43 && SSB
+       depends on B43 && (SSB = y || SSB = B43)
        default y
 
 # Auto-select SSB PCI-HOST support, if possible
@@ -111,6 +111,7 @@ config B43_PIO
 config B43_PHY_N
        bool "Support for 802.11n (N-PHY) devices"
        depends on B43
+       default y
        ---help---
          Support for the N-PHY.
 
@@ -132,6 +133,7 @@ config B43_PHY_LP
 config B43_PHY_HT
        bool "Support for HT-PHY (high throughput) devices"
        depends on B43 && B43_BCMA
+       default y
        ---help---
          Support for the HT-PHY.
 
index 4891e3d..3f8e69c 100644 (file)
 #include <linux/pci_ids.h>
 #include <linux/sched.h>
 #include <linux/completion.h>
+#include <linux/scatterlist.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
 
 #include <defs.h>
@@ -303,6 +305,153 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                *ret = retval;
 }
 
+/**
+ * brcmf_sdio_buffrw - SDIO interface function for block data access
+ * @sdiodev: brcmfmac sdio device
+ * @fn: SDIO function number
+ * @write: direction flag
+ * @addr: dongle memory address as source/destination
+ * @pkt: skb pointer
+ *
+ * This function takes the respbonsibility as the interface function to MMC
+ * stack for block data access. It assumes that the skb passed down by the
+ * caller has already been padded and aligned.
+ */
+static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                            bool write, u32 addr, struct sk_buff_head *pktlist)
+{
+       unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
+       unsigned int max_blks, max_req_sz;
+       unsigned short max_seg_sz, seg_sz;
+       unsigned char *pkt_data;
+       struct sk_buff *pkt_next = NULL;
+       struct mmc_request mmc_req;
+       struct mmc_command mmc_cmd;
+       struct mmc_data mmc_dat;
+       struct sg_table st;
+       struct scatterlist *sgl;
+       struct mmc_host *host;
+       int ret = 0;
+
+       if (!pktlist->qlen)
+               return -EINVAL;
+
+       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_pm_resume_error(sdiodev))
+               return -EIO;
+
+       /* Single skb use the standard mmc interface */
+       if (pktlist->qlen == 1) {
+               pkt_next = pktlist->next;
+               req_sz = pkt_next->len + 3;
+               req_sz &= (uint)~3;
+
+               if (write)
+                       return sdio_memcpy_toio(sdiodev->func[fn], addr,
+                                               ((u8 *)(pkt_next->data)),
+                                               req_sz);
+               else if (fn == 1)
+                       return sdio_memcpy_fromio(sdiodev->func[fn],
+                                                 ((u8 *)(pkt_next->data)),
+                                                 addr, req_sz);
+               else
+                       /* function 2 read is FIFO operation */
+                       return sdio_readsb(sdiodev->func[fn],
+                                          ((u8 *)(pkt_next->data)), addr,
+                                          req_sz);
+       }
+
+       host = sdiodev->func[fn]->card->host;
+       func_blk_sz = sdiodev->func[fn]->cur_blksize;
+       /* Blocks per command is limited by host count, host transfer
+        * size and the maximum for IO_RW_EXTENDED of 511 blocks.
+        */
+       max_blks = min_t(unsigned int, host->max_blk_count, 511u);
+       max_req_sz = min_t(unsigned int, host->max_req_size,
+                          max_blks * func_blk_sz);
+       max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC);
+       max_seg_sz = min_t(unsigned short, max_seg_sz, pktlist->qlen);
+       seg_sz = pktlist->qlen;
+       pkt_offset = 0;
+       pkt_next = pktlist->next;
+
+       if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL))
+               return -ENOMEM;
+
+       while (seg_sz) {
+               req_sz = 0;
+               sg_cnt = 0;
+               memset(&mmc_req, 0, sizeof(struct mmc_request));
+               memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+               memset(&mmc_dat, 0, sizeof(struct mmc_data));
+               sgl = st.sgl;
+               /* prep sg table */
+               while (pkt_next != (struct sk_buff *)pktlist) {
+                       pkt_data = pkt_next->data + pkt_offset;
+                       sg_data_sz = pkt_next->len - pkt_offset;
+                       if (sg_data_sz > host->max_seg_size)
+                               sg_data_sz = host->max_seg_size;
+                       if (sg_data_sz > max_req_sz - req_sz)
+                               sg_data_sz = max_req_sz - req_sz;
+
+                       sg_set_buf(sgl, pkt_data, sg_data_sz);
+
+                       sg_cnt++;
+                       sgl = sg_next(sgl);
+                       req_sz += sg_data_sz;
+                       pkt_offset += sg_data_sz;
+                       if (pkt_offset == pkt_next->len) {
+                               pkt_offset = 0;
+                               pkt_next = pkt_next->next;
+                       }
+
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz)
+                               break;
+               }
+               seg_sz -= sg_cnt;
+
+               if (req_sz % func_blk_sz != 0) {
+                       brcmf_err("sg request length %u is not %u aligned\n",
+                                 req_sz, func_blk_sz);
+                       sg_free_table(&st);
+                       return -ENOTBLK;
+               }
+               mmc_dat.sg = st.sgl;
+               mmc_dat.sg_len = sg_cnt;
+               mmc_dat.blksz = func_blk_sz;
+               mmc_dat.blocks = req_sz / func_blk_sz;
+               mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+               mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+               mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
+               mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
+               mmc_cmd.arg |= 1<<27;                   /* block mode */
+               /* incrementing addr for function 1 */
+               mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+               mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
+               mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
+               mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+               mmc_req.cmd = &mmc_cmd;
+               mmc_req.data = &mmc_dat;
+               if (fn == 1)
+                       addr += req_sz;
+
+               mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
+               mmc_wait_for_req(host, &mmc_req);
+
+               ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
+               if (ret != 0) {
+                       brcmf_err("CMD53 sg block %s failed %d\n",
+                                 write ? "write" : "read", ret);
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+       sg_free_table(&st);
+
+       return ret;
+}
+
 static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn,
                                     uint flags, uint width, u32 *addr)
 {
@@ -355,9 +504,9 @@ int
 brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        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);
@@ -367,9 +516,10 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
-                                        fn, addr, pkt);
+       skb_queue_head_init(&pkt_list);
+       skb_queue_tail(&pkt_list, pkt);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list);
+       skb_dequeue_tail(&pkt_list);
 
 done:
        return err;
@@ -391,8 +541,7 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                goto done;
 
        incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr,
-                                       pktq);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq);
 
 done:
        return err;
@@ -424,10 +573,10 @@ int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        uint width;
        uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
        int err = 0;
+       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
@@ -446,13 +595,14 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 
        addr &= SBSDIO_SB_OFT_ADDR_MASK;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
        if (width == 4)
                addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
 
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
-                                        addr, pkt);
+       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);
 
 done:
        return err;
@@ -466,6 +616,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        struct sk_buff *pkt;
        u32 sdaddr;
        uint dsize;
+       struct sk_buff_head pkt_list;
 
        dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
        pkt = dev_alloc_skb(dsize);
@@ -474,6 +625,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                return -EIO;
        }
        pkt->priority = 0;
+       skb_queue_head_init(&pkt_list);
 
        /* Determine initial transfer parameters */
        sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
@@ -501,9 +653,10 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                skb_put(pkt, dsize);
                if (write)
                        memcpy(pkt->data, data, dsize);
-               bcmerror = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC,
-                                                     write, SDIO_FUNC_1,
-                                                     sdaddr, pkt);
+               skb_queue_tail(&pkt_list, pkt);
+               bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
+                                            sdaddr, &pkt_list);
+               skb_dequeue_tail(&pkt_list);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
index 11400b3..289e386 100644 (file)
@@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
 static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
 
 
-static bool
+bool
 brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
 {
        bool is_err = false;
@@ -76,7 +76,7 @@ brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
        return is_err;
 }
 
-static void
+void
 brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
 {
 #ifdef CONFIG_PM_SLEEP
@@ -211,115 +211,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
        return err_ret;
 }
 
-/* precondition: host controller is claimed */
-static int
-brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo,
-                        uint func, uint addr, struct sk_buff *pkt, uint pktlen)
-{
-       int err_ret = 0;
-
-       if ((write) && (!fifo)) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (write) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (fifo) {
-               err_ret = sdio_readsb(sdiodev->func[func],
-                                     ((u8 *) (pkt->data)), addr, pktlen);
-       } else {
-               err_ret = sdio_memcpy_fromio(sdiodev->func[func],
-                                            ((u8 *) (pkt->data)),
-                                            addr, pktlen);
-       }
-
-       return err_ret;
-}
-
-/*
- * This function takes a queue of packets. The packets on the queue
- * are assumed to be properly aligned by the caller.
- */
-int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq)
-{
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-       u32 SGCount = 0;
-       int err_ret = 0;
-
-       struct sk_buff *pkt;
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       skb_queue_walk(pktq, pkt) {
-               uint pkt_len = pkt->len;
-               pkt_len += 3;
-               pkt_len &= 0xFFFFFFFC;
-
-               err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                                  addr, pkt, pkt_len);
-               if (err_ret) {
-                       brcmf_err("%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len, err_ret);
-               } else {
-                       brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len);
-               }
-               if (!fifo)
-                       addr += pkt_len;
-
-               SGCount++;
-       }
-
-       brcmf_dbg(SDIO, "Exit\n");
-       return err_ret;
-}
-
-/*
- * This function takes a single DMA-able packet.
- */
-int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                              uint fix_inc, uint write, uint func, uint addr,
-                              struct sk_buff *pkt)
-{
-       int status;
-       uint pkt_len;
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       if (pkt == NULL)
-               return -EINVAL;
-       pkt_len = pkt->len;
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       pkt_len += 3;
-       pkt_len &= (uint)~3;
-
-       status = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                          addr, pkt, pkt_len);
-       if (status) {
-               brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len, status);
-       } else {
-               brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len);
-       }
-
-       return status;
-}
-
 static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
 {
        /* read 24 bits and return valid 17 bit addr */
@@ -468,7 +359,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        atomic_set(&sdiodev->suspend, false);
        init_waitqueue_head(&sdiodev->request_byte_wait);
        init_waitqueue_head(&sdiodev->request_word_wait);
-       init_waitqueue_head(&sdiodev->request_chain_wait);
        init_waitqueue_head(&sdiodev->request_buffer_wait);
 
        brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
index 28db9cf..86cbfe2 100644 (file)
@@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason {
  * @bssidx: index of bss associated with this interface.
  * @mac_addr: assigned mac address.
  * @netif_stop: bitmap indicates reason why netif queues are stopped.
+ * @netif_stop_lock: spinlock for update netif_stop from multiple sources.
  * @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
  * @pend_8021x_wait: used for signalling change in count.
  */
@@ -598,6 +599,7 @@ struct brcmf_if {
        s32 bssidx;
        u8 mac_addr[ETH_ALEN];
        u8 netif_stop;
+       spinlock_t netif_stop_lock;
        atomic_t pend_8021x_cnt;
        wait_queue_head_t pend_8021x_wait;
 };
index 59c77aa..dd85401 100644 (file)
@@ -30,6 +30,7 @@
 #include "dhd_bus.h"
 #include "fwsignal.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 
 struct brcmf_proto_cdc_dcmd {
        __le32 cmd;     /* dongle command value */
@@ -292,6 +293,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
        h->flags2 = 0;
        h->data_offset = offset;
        BDC_SET_IF_IDX(h, ifidx);
+       trace_brcmf_bdchdr(pktbuf->data);
 }
 
 int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
@@ -309,6 +311,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                return -EBADE;
        }
 
+       trace_brcmf_bdchdr(pktbuf->data);
        h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
 
        *ifidx = BDC_GET_IF_IDX(h);
index 202869c..c37b9d6 100644 (file)
@@ -156,8 +156,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        "txs_suppr_core:    %u\n"
                        "txs_suppr_ps:      %u\n"
                        "txs_tossed:        %u\n"
+                       "txs_host_tossed:   %u\n"
+                       "bus_flow_block:    %u\n"
+                       "fws_flow_block:    %u\n"
                        "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
-                       "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                       "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
                        fwstats->header_pulls,
                        fwstats->header_only_pkt,
                        fwstats->tlv_parse_failed,
@@ -176,14 +179,17 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        fwstats->txs_supp_core,
                        fwstats->txs_supp_ps,
                        fwstats->txs_tossed,
+                       fwstats->txs_host_tossed,
+                       fwstats->bus_flow_block,
+                       fwstats->fws_flow_block,
                        fwstats->send_pkts[0], fwstats->send_pkts[1],
                        fwstats->send_pkts[2], fwstats->send_pkts[3],
                        fwstats->send_pkts[4],
-                       fwstats->fifo_credits_sent[0],
-                       fwstats->fifo_credits_sent[1],
-                       fwstats->fifo_credits_sent[2],
-                       fwstats->fifo_credits_sent[3],
-                       fwstats->fifo_credits_sent[4]);
+                       fwstats->requested_sent[0],
+                       fwstats->requested_sent[1],
+                       fwstats->requested_sent[2],
+                       fwstats->requested_sent[3],
+                       fwstats->requested_sent[4]);
 
        return simple_read_from_buffer(data, count, ppos, buf, res);
 }
index 009c87b..0af1f5d 100644 (file)
@@ -141,8 +141,7 @@ struct brcmf_fws_stats {
        u32 header_pulls;
        u32 pkt2bus;
        u32 send_pkts[5];
-       u32 fifo_credits_sent[5];
-       u32 fifo_credits_back[6];
+       u32 requested_sent[5];
        u32 generic_error;
        u32 mac_update_failed;
        u32 mac_ps_update_failed;
@@ -158,6 +157,9 @@ struct brcmf_fws_stats {
        u32 txs_supp_core;
        u32 txs_supp_ps;
        u32 txs_tossed;
+       u32 txs_host_tossed;
+       u32 bus_flow_block;
+       u32 fws_flow_block;
 };
 
 struct brcmf_pub;
index 2c59357..8c402e7 100644 (file)
@@ -179,7 +179,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
        struct brcmf_pub *drvr = ifp->drvr;
        struct ethhdr *eh;
 
-       brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+       brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
        /* Can the device send data? */
        if (drvr->bus_if->state != BRCMF_BUS_DATA) {
@@ -240,11 +240,15 @@ done:
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state)
 {
+       unsigned long flags;
+
        if (!ifp)
                return;
 
        brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
                  ifp->bssidx, ifp->netif_stop, reason, state);
+
+       spin_lock_irqsave(&ifp->netif_stop_lock, flags);
        if (state) {
                if (!ifp->netif_stop)
                        netif_stop_queue(ifp->ndev);
@@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
                if (!ifp->netif_stop)
                        netif_wake_queue(ifp->ndev);
        }
+       spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
 }
 
 void brcmf_txflowblock(struct device *dev, bool state)
@@ -264,9 +269,14 @@ void brcmf_txflowblock(struct device *dev, bool state)
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       for (i = 0; i < BRCMF_MAX_IFS; i++)
-               brcmf_txflowblock_if(drvr->iflist[i],
-                                    BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
+       if (brcmf_fws_fc_active(drvr->fws)) {
+               brcmf_fws_bus_blocked(drvr, state);
+       } else {
+               for (i = 0; i < BRCMF_MAX_IFS; i++)
+                       brcmf_txflowblock_if(drvr->iflist[i],
+                                            BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
+                                            state);
+       }
 }
 
 void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
@@ -280,7 +290,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        u8 ifidx;
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(DATA, "Enter\n");
 
        skb_queue_walk_safe(skb_list, skb, pnext) {
                skb_unlink(skb, skb_list);
@@ -630,7 +640,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
        /* set appropriate operations */
        ndev->netdev_ops = &brcmf_netdev_ops_pri;
 
-       ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
+       ndev->hard_header_len += drvr->hdrlen;
        ndev->ethtool_ops = &brcmf_ethtool_ops;
 
        drvr->rxsz = ndev->mtu + ndev->hard_header_len +
@@ -779,6 +789,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
        ifp->bssidx = bssidx;
 
        init_waitqueue_head(&ifp->pend_8021x_wait);
+       spin_lock_init(&ifp->netif_stop_lock);
 
        if (mac_addr != NULL)
                memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
index d248751..2641119 100644 (file)
@@ -448,8 +448,6 @@ struct brcmf_sdio {
        uint rxblen;            /* Allocated length of rxbuf */
        u8 *rxctl;              /* Aligned pointer into rxbuf */
        u8 *rxctl_orig;         /* pointer for freeing rxctl */
-       u8 *databuf;            /* Buffer for receiving big glom packet */
-       u8 *dataptr;            /* Aligned pointer into databuf */
        uint rxlen;             /* Length of valid data in buffer */
        spinlock_t rxctl_lock;  /* protection lock for ctrl frame resources */
 
@@ -473,8 +471,6 @@ struct brcmf_sdio {
        s32 idletime;           /* Control for activity timeout */
        s32 idlecount;  /* Activity timeout counter */
        s32 idleclock;  /* How to set bus driver when idle */
-       s32 sd_rxchain;
-       bool use_rxchain;       /* If brcmf should use PKT chains */
        bool rxflow_mode;       /* Rx flow control mode */
        bool rxflow;            /* Is rx flow control on */
        bool alp_only;          /* Don't use HT clock (ALP only) */
@@ -495,8 +491,7 @@ struct brcmf_sdio {
 
        struct workqueue_struct *brcmf_wq;
        struct work_struct datawork;
-       struct list_head dpc_tsklst;
-       spinlock_t dpc_tl_lock;
+       atomic_t dpc_tskcnt;
 
        const struct firmware *firmware;
        u32 fw_ptr;
@@ -1026,29 +1021,6 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
                bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
 }
 
-/* copy a buffer into a pkt buffer chain */
-static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len)
-{
-       uint n, ret = 0;
-       struct sk_buff *p;
-       u8 *buf;
-
-       buf = bus->dataptr;
-
-       /* copy the data */
-       skb_queue_walk(&bus->glom, p) {
-               n = min_t(uint, p->len, len);
-               memcpy(p->data, buf, n);
-               buf += n;
-               len -= n;
-               ret += n;
-               if (!len)
-                       break;
-       }
-
-       return ret;
-}
-
 /* return total length of buffer chain */
 static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
 {
@@ -1202,8 +1174,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        int errcode;
        u8 doff, sfdoff;
 
-       bool usechain = bus->use_rxchain;
-
        struct brcmf_sdio_read rd_new;
 
        /* If packets, issue read(s) and send up packet chain */
@@ -1238,7 +1208,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        if (sublen % BRCMF_SDALIGN) {
                                brcmf_err("sublen %d not multiple of %d\n",
                                          sublen, BRCMF_SDALIGN);
-                               usechain = false;
                        }
                        totlen += sublen;
 
@@ -1305,27 +1274,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                 * packet and and copy into the chain.
                 */
                sdio_claim_host(bus->sdiodev->func[1]);
-               if (usechain) {
-                       errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC, &bus->glom);
-               } else if (bus->dataptr) {
-                       errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC,
-                                       bus->dataptr, dlen);
-                       sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen);
-                       if (sublen != dlen) {
-                               brcmf_err("FAILED TO COPY, dlen %d sublen %d\n",
-                                         dlen, sublen);
-                               errcode = -1;
-                       }
-                       pnext = NULL;
-               } else {
-                       brcmf_err("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
-                                 dlen);
-                       errcode = -1;
-               }
+               errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
+                               bus->sdiodev->sbwad,
+                               SDIO_FUNC_2, F2SYNC, &bus->glom);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -2061,23 +2012,6 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
        }
 }
 
-static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
-{
-       struct list_head *new_hd;
-       unsigned long flags;
-
-       if (in_interrupt())
-               new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
-       else
-               new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
-       if (new_hd == NULL)
-               return;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_add_tail(new_hd, &bus->dpc_tsklst);
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-}
-
 static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
 {
        u8 idx;
@@ -2312,7 +2246,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                   (!atomic_read(&bus->fcstate) &&
                    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
                    data_ok(bus)) || PKT_AVAILABLE()) {
-               brcmf_sdbrcm_adddpctsk(bus);
+               atomic_inc(&bus->dpc_tskcnt);
        }
 
        /* If we're done for now, turn off clock request. */
@@ -2342,7 +2276,6 @@ 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;
-       unsigned long flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2369,26 +2302,21 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        } else {
                ret = 0;
        }
-       spin_unlock_bh(&bus->txqlock);
 
        if (pktq_len(&bus->txq) >= TXHI) {
                bus->txoff = true;
                brcmf_txflowblock(bus->sdiodev->dev, true);
        }
+       spin_unlock_bh(&bus->txqlock);
 
 #ifdef DEBUG
        if (pktq_plen(&bus->txq, prec) > qcount[prec])
                qcount[prec] = pktq_plen(&bus->txq, prec);
 #endif
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       if (list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
-               brcmf_sdbrcm_adddpctsk(bus);
+       if (atomic_read(&bus->dpc_tskcnt) == 0) {
+               atomic_inc(&bus->dpc_tskcnt);
                queue_work(bus->brcmf_wq, &bus->datawork);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        return ret;
@@ -2525,7 +2453,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
        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;
-       unsigned long flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2612,18 +2539,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                } while (ret < 0 && retries++ < TXRETRIES);
        }
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
        if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
-           list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
+           atomic_read(&bus->dpc_tskcnt) == 0) {
                bus->activity = false;
                sdio_claim_host(bus->sdiodev->func[1]);
                brcmf_dbg(INFO, "idle\n");
                brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
                sdio_release_host(bus->sdiodev->func[1]);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        if (ret)
@@ -3451,7 +3373,7 @@ void brcmf_sdbrcm_isr(void *arg)
        if (!bus->intr)
                brcmf_err("isr w/o interrupt configured!\n");
 
-       brcmf_sdbrcm_adddpctsk(bus);
+       atomic_inc(&bus->dpc_tskcnt);
        queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
@@ -3460,7 +3382,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
 #ifdef DEBUG
        struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
 #endif /* DEBUG */
-       unsigned long flags;
 
        brcmf_dbg(TIMER, "Enter\n");
 
@@ -3476,11 +3397,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                if (!bus->intr ||
                    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 
-                       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-                       if (list_empty(&bus->dpc_tsklst)) {
+                       if (atomic_read(&bus->dpc_tskcnt) == 0) {
                                u8 devpend;
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
+
                                sdio_claim_host(bus->sdiodev->func[1]);
                                devpend = brcmf_sdio_regrb(bus->sdiodev,
                                                           SDIO_CCCR_INTx,
@@ -3489,9 +3408,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                intstatus =
                                    devpend & (INTR_STATUS_FUNC1 |
                                               INTR_STATUS_FUNC2);
-                       } else {
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
                        }
 
                        /* If there is something, make like the ISR and
@@ -3500,7 +3416,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                bus->sdcnt.pollcnt++;
                                atomic_set(&bus->ipend, 1);
 
-                               brcmf_sdbrcm_adddpctsk(bus);
+                               atomic_inc(&bus->dpc_tskcnt);
                                queue_work(bus->brcmf_wq, &bus->datawork);
                        }
                }
@@ -3545,41 +3461,15 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
        return (atomic_read(&bus->ipend) > 0);
 }
 
-static bool brcmf_sdbrcm_chipmatch(u16 chipid)
-{
-       if (chipid == BCM43143_CHIP_ID)
-               return true;
-       if (chipid == BCM43241_CHIP_ID)
-               return true;
-       if (chipid == BCM4329_CHIP_ID)
-               return true;
-       if (chipid == BCM4330_CHIP_ID)
-               return true;
-       if (chipid == BCM4334_CHIP_ID)
-               return true;
-       if (chipid == BCM4335_CHIP_ID)
-               return true;
-       return false;
-}
-
 static void brcmf_sdio_dataworker(struct work_struct *work)
 {
        struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
                                              datawork);
-       struct list_head *cur_hd, *tmp_hd;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 
+       while (atomic_read(&bus->dpc_tskcnt)) {
                brcmf_sdbrcm_dpc(bus);
-
-               spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-               list_del(cur_hd);
-               kfree(cur_hd);
+               atomic_dec(&bus->dpc_tskcnt);
        }
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 }
 
 static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
@@ -3589,9 +3479,6 @@ static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
        kfree(bus->rxbuf);
        bus->rxctl = bus->rxbuf = NULL;
        bus->rxlen = 0;
-
-       kfree(bus->databuf);
-       bus->databuf = NULL;
 }
 
 static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
@@ -3604,29 +3491,10 @@ static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
                            ALIGNMENT) + BRCMF_SDALIGN;
                bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
                if (!(bus->rxbuf))
-                       goto fail;
-       }
-
-       /* Allocate buffer to receive glomed packet */
-       bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
-       if (!(bus->databuf)) {
-               /* release rxbuf which was already located as above */
-               if (!bus->rxblen)
-                       kfree(bus->rxbuf);
-               goto fail;
+                       return false;
        }
 
-       /* Align the buffer */
-       if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
-               bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
-                              ((unsigned long)bus->databuf % BRCMF_SDALIGN));
-       else
-               bus->dataptr = bus->databuf;
-
        return true;
-
-fail:
-       return false;
 }
 
 static bool
@@ -3667,11 +3535,6 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
                goto fail;
        }
 
-       if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
-               brcmf_err("unsupported chip: 0x%04x\n", bus->ci->chip);
-               goto fail;
-       }
-
        if (brcmf_sdbrcm_kso_init(bus)) {
                brcmf_err("error enabling KSO\n");
                goto fail;
@@ -3770,10 +3633,6 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
        bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
        bus->roundup = min(max_roundup, bus->blocksize);
 
-       /* bus module does not support packet chaining */
-       bus->use_rxchain = false;
-       bus->sd_rxchain = false;
-
        /* SR state */
        bus->sleeping = false;
        bus->sr_enabled = false;
@@ -3927,8 +3786,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
                bus->watchdog_tsk = NULL;
        }
        /* Initialize DPC thread */
-       INIT_LIST_HEAD(&bus->dpc_tsklst);
-       spin_lock_init(&bus->dpc_tl_lock);
+       atomic_set(&bus->dpc_tskcnt, 0);
 
        /* Assign bus interface call back */
        bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
index 6ec5db9..e679214 100644 (file)
@@ -101,7 +101,8 @@ struct brcmf_event;
        BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \
        BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
-       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75)
+       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
 
 #define BRCMF_ENUM_DEF(id, val) \
        BRCMF_E_##id = (val),
index 5352dc1..13e75c4 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/etherdevice.h>
 #include <linux/err.h>
 #include <linux/jiffies.h>
-#include <uapi/linux/nl80211.h>
 #include <net/cfg80211.h>
 
 #include <brcmu_utils.h>
@@ -142,7 +141,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
 #define BRCMF_FWS_FLOWCONTROL_HIWATER                  128
 #define BRCMF_FWS_FLOWCONTROL_LOWATER                  64
 
-#define BRCMF_FWS_PSQ_PREC_COUNT               ((NL80211_NUM_ACS + 1) * 2)
+#define BRCMF_FWS_PSQ_PREC_COUNT               ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
 #define BRCMF_FWS_PSQ_LEN                              256
 
 #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST                        0x01
@@ -157,11 +156,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
  * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
  * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
  * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
+ * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
  */
 enum brcmf_fws_skb_state {
        BRCMF_FWS_SKBSTATE_NEW,
        BRCMF_FWS_SKBSTATE_DELAYED,
-       BRCMF_FWS_SKBSTATE_SUPPRESSED
+       BRCMF_FWS_SKBSTATE_SUPPRESSED,
+       BRCMF_FWS_SKBSTATE_TIM
 };
 
 /**
@@ -193,9 +194,8 @@ struct brcmf_skbuff_cb {
  *     b[11]  - packet sent upon firmware request.
  *     b[10]  - packet only contains signalling data.
  *     b[9]   - packet is a tx packet.
- *     b[8]   - packet uses FIFO credit (non-pspoll).
+ *     b[8]   - packet used requested credit
  *     b[7]   - interface in AP mode.
- *     b[6:4] - AC FIFO number.
  *     b[3:0] - interface index.
  */
 #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK      0x0800
@@ -204,12 +204,10 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT   10
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK        0x0200
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT      9
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK    0x0100
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT   8
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK     0x0100
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT    8
 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK          0x0080
 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT         7
-#define BRCMF_SKB_IF_FLAGS_FIFO_MASK           0x0070
-#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT          4
 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK          0x000f
 #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT         0
 
@@ -246,7 +244,7 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK                  0x00ffff00
 #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT                 8
 #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK                        0x000000ff
-#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT                       0
+#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT               0
 
 #define brcmf_skb_htod_tag_set_field(skb, field, value) \
        brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
@@ -278,6 +276,7 @@ struct brcmf_skbuff_cb {
 /**
  * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
  *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
  * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
  * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
  * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
@@ -287,7 +286,8 @@ struct brcmf_skbuff_cb {
  * @BRCMF_FWS_FIFO_COUNT: number of fifos.
  */
 enum brcmf_fws_fifo {
-       BRCMF_FWS_FIFO_AC_BK,
+       BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
        BRCMF_FWS_FIFO_AC_BE,
        BRCMF_FWS_FIFO_AC_VI,
        BRCMF_FWS_FIFO_AC_VO,
@@ -307,12 +307,15 @@ enum brcmf_fws_fifo {
  *     firmware suppress the packet as device is already in PS mode.
  * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
  *     firmware tossed the packet.
+ * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
+ *     host tossed the packet.
  */
 enum brcmf_fws_txstatus {
        BRCMF_FWS_TXSTATUS_DISCARD,
        BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
        BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
-       BRCMF_FWS_TXSTATUS_FW_TOSSED
+       BRCMF_FWS_TXSTATUS_FW_TOSSED,
+       BRCMF_FWS_TXSTATUS_HOST_TOSSED
 };
 
 enum brcmf_fws_fcmode {
@@ -343,6 +346,7 @@ enum brcmf_fws_mac_desc_state {
  * @transit_count: packet in transit to firmware.
  */
 struct brcmf_fws_mac_descriptor {
+       char name[16];
        u8 occupied;
        u8 mac_handle;
        u8 interface_id;
@@ -356,7 +360,6 @@ struct brcmf_fws_mac_descriptor {
        u8 seq[BRCMF_FWS_FIFO_COUNT];
        struct pktq psq;
        int transit_count;
-       int suppress_count;
        int suppr_transit_count;
        bool send_tim_signal;
        u8 traffic_pending_bmp;
@@ -383,12 +386,10 @@ enum brcmf_fws_hanger_item_state {
  * struct brcmf_fws_hanger_item - single entry for tx pending packet.
  *
  * @state: entry is either free or occupied.
- * @gen: generation.
  * @pkt: packet itself.
  */
 struct brcmf_fws_hanger_item {
        enum brcmf_fws_hanger_item_state state;
-       u8 gen;
        struct sk_buff *pkt;
 };
 
@@ -424,6 +425,7 @@ struct brcmf_fws_info {
        struct brcmf_fws_stats stats;
        struct brcmf_fws_hanger hanger;
        enum brcmf_fws_fcmode fcmode;
+       bool bcmc_credit_check;
        struct brcmf_fws_macdesc_table desc;
        struct workqueue_struct *fws_wq;
        struct work_struct fws_dequeue_work;
@@ -434,6 +436,8 @@ struct brcmf_fws_info {
        u32 fifo_credit_map;
        u32 fifo_delay_map;
        unsigned long borrow_defer_timestamp;
+       bool bus_flow_blocked;
+       bool creditmap_received;
 };
 
 /*
@@ -507,7 +511,6 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
 {
        int i;
 
-       brcmf_dbg(TRACE, "enter\n");
        memset(hanger, 0, sizeof(*hanger));
        for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
                hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
@@ -517,7 +520,6 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
 {
        u32 i;
 
-       brcmf_dbg(TRACE, "enter\n");
        i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
 
        while (i != h->slot_pos) {
@@ -533,14 +535,12 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
        h->failed_slotfind++;
        i = BRCMF_FWS_HANGER_MAXITEMS;
 done:
-       brcmf_dbg(TRACE, "exit: %d\n", i);
        return i;
 }
 
 static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
-                                          struct sk_buff *pkt, u32 slot_id)
+                                   struct sk_buff *pkt, u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -560,7 +560,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
                                          u32 slot_id, struct sk_buff **pktout,
                                          bool remove_item)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -574,23 +573,18 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
        if (remove_item) {
                h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
                h->items[slot_id].pkt = NULL;
-               h->items[slot_id].gen = 0xff;
                h->popped++;
        }
        return 0;
 }
 
 static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
-                                                  u32 slot_id, u8 gen)
+                                           u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
-
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
-       h->items[slot_id].gen = gen;
-
-       if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) {
+       if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
                brcmf_err("entry not in use\n");
                return -EINVAL;
        }
@@ -599,25 +593,6 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
        return 0;
 }
 
-static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger,
-                                             struct sk_buff *pkt, u32 slot_id,
-                                             int *gen)
-{
-       brcmf_dbg(TRACE, "enter\n");
-       *gen = 0xff;
-
-       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
-               return -ENOENT;
-
-       if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
-               brcmf_err("slot not in use\n");
-               return -EINVAL;
-       }
-
-       *gen = hanger->items[slot_id].gen;
-       return 0;
-}
-
 static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
                                     bool (*fn)(struct sk_buff *, void *),
                                     int ifidx)
@@ -627,7 +602,6 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        int i;
        enum brcmf_fws_hanger_item_state s;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        for (i = 0; i < ARRAY_SIZE(h->items); i++) {
                s = h->items[i].state;
                if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
@@ -644,14 +618,28 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        }
 }
 
-static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
-                                         u8 *addr, u8 ifidx)
+static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
+                                      struct brcmf_fws_mac_descriptor *desc)
+{
+       if (desc == &fws->desc.other)
+               strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
+       else if (desc->mac_handle)
+               scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
+                         desc->mac_handle, desc->interface_id);
+       else
+               scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
+                         desc->interface_id);
+}
+
+static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
+                                  u8 *addr, u8 ifidx)
 {
        brcmf_dbg(TRACE,
                  "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
        desc->occupied = 1;
        desc->state = BRCMF_FWS_STATE_OPEN;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
        /* depending on use may need ifp->bssidx instead */
        desc->interface_id = ifidx;
        desc->ac_bitmap = 0xff; /* update this when handling APSD */
@@ -660,22 +648,22 @@ static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
 }
 
 static
-void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc)
+void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
 {
        brcmf_dbg(TRACE,
                  "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
        desc->occupied = 0;
        desc->state = BRCMF_FWS_STATE_CLOSE;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
 }
 
 static struct brcmf_fws_mac_descriptor *
-brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
+brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
 {
        struct brcmf_fws_mac_descriptor *entry;
        int i;
 
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", ea);
        if (ea == NULL)
                return ERR_PTR(-EINVAL);
 
@@ -690,42 +678,33 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
 }
 
 static struct brcmf_fws_mac_descriptor*
-brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp,
-                       u8 *da)
+brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
 {
        struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
        bool multicast;
-       enum nl80211_iftype iftype;
-
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
 
        multicast = is_multicast_ether_addr(da);
-       iftype = brcmf_cfg80211_get_iftype(ifp);
 
-       /* Multicast destination and P2P clients get the interface entry.
-        * STA gets the interface entry if there is no exact match. For
-        * example, TDLS destinations have their own entry.
+       /* Multicast destination, STA and P2P clients get the interface entry.
+        * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
+        * have their own entry.
         */
-       entry = NULL;
-       if ((multicast || iftype == NL80211_IFTYPE_STATION ||
-            iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc)
+       if (multicast && ifp->fws_desc) {
                entry = ifp->fws_desc;
-
-       if (entry != NULL && iftype != NL80211_IFTYPE_STATION)
                goto done;
+       }
 
-       entry = brcmf_fws_mac_descriptor_lookup(fws, da);
+       entry = brcmf_fws_macdesc_lookup(fws, da);
        if (IS_ERR(entry))
-               entry = &fws->desc.other;
+               entry = ifp->fws_desc;
 
 done:
-       brcmf_dbg(TRACE, "exit: entry=%p\n", entry);
        return entry;
 }
 
-static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
-                                     struct brcmf_fws_mac_descriptor *entry,
-                                     int fifo)
+static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
+                                    struct brcmf_fws_mac_descriptor *entry,
+                                    int fifo)
 {
        struct brcmf_fws_mac_descriptor *if_entry;
        bool closed;
@@ -748,15 +727,11 @@ static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
        return closed || !(entry->ac_bitmap & BIT(fifo));
 }
 
-static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws,
-                                      struct brcmf_fws_mac_descriptor *entry,
-                                      int ifidx)
+static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
+                                     struct brcmf_fws_mac_descriptor *entry,
+                                     int ifidx)
 {
-       brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n",
-                 entry->ea, entry->interface_id, ifidx);
        if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
-               brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n",
-                         ifidx, entry->psq.len);
                brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
                entry->occupied = !!(entry->psq.len);
        }
@@ -772,7 +747,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
        int prec;
        u32 hslot;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        txq = brcmf_bus_gettxq(fws->drvr->bus_if);
        if (IS_ERR(txq)) {
                brcmf_dbg(TRACE, "no txq to clean up\n");
@@ -798,7 +772,6 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        struct brcmf_fws_mac_descriptor *table;
        bool (*matchfn)(struct sk_buff *, void *) = NULL;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (fws == NULL)
                return;
 
@@ -808,51 +781,122 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        /* cleanup individual nodes */
        table = &fws->desc.nodes[0];
        for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
-               brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx);
+               brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
 
-       brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx);
+       brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
        brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
        brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
 }
 
-static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx,
-                                struct brcmf_fws_mac_descriptor *entry,
-                                int prec)
+static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea);
-       if (entry->state == BRCMF_FWS_STATE_CLOSE) {
-               /* check delayedQ and suppressQ in one call using bitmap */
-               if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp & ~NBITVAL(prec);
-               else
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp | NBITVAL(prec);
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       u8 *wlh;
+       u16 data_offset = 0;
+       u8 fillers;
+       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+
+       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u (%u), pkttag=0x%08X, hslot=%d\n",
+                 entry->ea, entry->interface_id,
+                 brcmf_skb_if_flags_get_field(skb, INDEX),
+                 le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff);
+       if (entry->send_tim_signal)
+               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+
+       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+       fillers = round_up(data_offset, 4) - data_offset;
+       data_offset += fillers;
+
+       skb_push(skb, data_offset);
+       wlh = skb->data;
+
+       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
+
+       if (entry->send_tim_signal) {
+               entry->send_tim_signal = 0;
+               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+               wlh[2] = entry->mac_handle;
+               wlh[3] = entry->traffic_pending_bmp;
+               brcmf_dbg(TRACE, "adding TIM info: %02X:%02X:%02X:%02X\n",
+                         wlh[0], wlh[1], wlh[2], wlh[3]);
+               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
        }
-       /* request a TIM update to firmware at the next piggyback opportunity */
+       if (fillers)
+               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
+
+       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
+                           data_offset >> 2, skb);
+       return 0;
+}
+
+static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
+                                struct brcmf_fws_mac_descriptor *entry,
+                                int fifo, bool send_immediately)
+{
+       struct sk_buff *skb;
+       struct brcmf_bus *bus;
+       struct brcmf_skbuff_cb *skcb;
+       s32 err;
+       u32 len;
+
+       /* check delayedQ and suppressQ in one call using bitmap */
+       if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
+               entry->traffic_pending_bmp &= ~NBITVAL(fifo);
+       else
+               entry->traffic_pending_bmp |= NBITVAL(fifo);
+
+       entry->send_tim_signal = false;
        if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
                entry->send_tim_signal = true;
+       if (send_immediately && entry->send_tim_signal &&
+           entry->state == BRCMF_FWS_STATE_CLOSE) {
+               /* create a dummy packet and sent that. The traffic          */
+               /* bitmap info will automatically be attached to that packet */
+               len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
+                     BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
+                     4 + fws->drvr->hdrlen;
+               skb = brcmu_pkt_buf_get_skb(len);
+               if (skb == NULL)
+                       return false;
+               skb_pull(skb, len);
+               skcb = brcmf_skbcb(skb);
+               skcb->mac = entry;
+               skcb->state = BRCMF_FWS_SKBSTATE_TIM;
+               bus = fws->drvr->bus_if;
+               err = brcmf_fws_hdrpush(fws, skb);
+               if (err == 0)
+                       err = brcmf_bus_txdata(bus, skb);
+               if (err)
+                       brcmu_pkt_buf_free_skb(skb);
+               return true;
+       }
+       return false;
 }
 
 static void
 brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
                             u8 if_id)
 {
-       struct brcmf_if *ifp = fws->drvr->iflist[if_id];
+       struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
 
        if (WARN_ON(!ifp))
                return;
 
-       brcmf_dbg(TRACE,
-                 "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
-
        if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
            pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
                brcmf_txflowblock_if(ifp,
                                     BRCMF_NETIF_STOP_REASON_FWS_FC, false);
        if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
-           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER)
+           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
+               fws->stats.fws_flow_block++;
                brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
+       }
        return;
 }
 
@@ -876,34 +920,38 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
 
        entry = &fws->desc.nodes[mac_handle & 0x1F];
        if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
-               brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
                if (entry->occupied) {
-                       brcmf_fws_mac_desc_cleanup(fws, entry, -1);
-                       brcmf_fws_clear_mac_descriptor(entry);
+                       brcmf_dbg(TRACE, "deleting %s mac %pM\n",
+                                 entry->name, addr);
+                       brcmf_fws_macdesc_cleanup(fws, entry, -1);
+                       brcmf_fws_macdesc_deinit(entry);
                } else
                        fws->stats.mac_update_failed++;
                return 0;
        }
 
-       brcmf_dbg(TRACE,
-                 "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx);
-       existing = brcmf_fws_mac_descriptor_lookup(fws, addr);
+       existing = brcmf_fws_macdesc_lookup(fws, addr);
        if (IS_ERR(existing)) {
                if (!entry->occupied) {
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_init_mac_descriptor(entry, addr, ifidx);
+                       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_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
                } else {
                        fws->stats.mac_update_failed++;
                }
        } else {
                if (entry != existing) {
-                       brcmf_dbg(TRACE, "relocate mac\n");
+                       brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
                        memcpy(entry, existing,
                               offsetof(struct brcmf_fws_mac_descriptor, psq));
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_clear_mac_descriptor(existing);
+                       brcmf_fws_macdesc_deinit(existing);
+                       brcmf_fws_macdesc_set_name(fws, entry);
+                       brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
+                                 addr);
                } else {
                        brcmf_dbg(TRACE, "use existing\n");
                        WARN_ON(entry->mac_handle != mac_handle);
@@ -918,7 +966,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
 {
        struct brcmf_fws_mac_descriptor *entry;
        u8 mac_handle;
-       int i;
 
        mac_handle = data[0];
        entry = &fws->desc.nodes[mac_handle & 0x1F];
@@ -926,16 +973,18 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                fws->stats.mac_ps_update_failed++;
                return -ESRCH;
        }
-
-       /* a state update should wipe old credits? */
+       /* a state update should wipe old credits */
        entry->requested_credit = 0;
+       entry->requested_packet = 0;
        if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
                entry->state = BRCMF_FWS_STATE_OPEN;
                return BRCMF_FWS_RET_OK_SCHEDULE;
        } else {
                entry->state = BRCMF_FWS_STATE_CLOSE;
-               for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
-                       brcmf_fws_tim_update(fws, entry, i);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
        }
        return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
@@ -949,7 +998,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
 
        ifidx = data[0];
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (ifidx >= BRCMF_MAX_IFS) {
                ret = -ERANGE;
                goto fail;
@@ -961,6 +1009,8 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                goto fail;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
+                 entry->name);
        switch (type) {
        case BRCMF_FWS_TYPE_INTERFACE_OPEN:
                entry->state = BRCMF_FWS_STATE_OPEN;
@@ -991,6 +1041,9 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
                return -ESRCH;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
+                 brcmf_fws_get_tlv_name(type), type, entry->name,
+                 data[0], data[2]);
        if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
                entry->requested_credit = data[0];
        else
@@ -1000,6 +1053,37 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
+static void
+brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
+                                struct sk_buff *skb)
+{
+       if (entry->requested_credit > 0) {
+               entry->requested_credit--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested credit set while mac not closed!\n");
+       } else if (entry->requested_packet > 0) {
+               entry->requested_packet--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested packet set while mac not closed!\n");
+       } else {
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+       }
+}
+
+static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+
+       if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
+           (entry->state == BRCMF_FWS_STATE_CLOSE))
+               entry->requested_credit++;
+}
+
 static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                                     u8 fifo, u8 credits)
 {
@@ -1010,6 +1094,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
        if (!credits)
                return;
 
+       fws->fifo_credit_map |= 1 << fifo;
+
        if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
            (fws->credits_borrowed[0])) {
                for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
@@ -1031,7 +1117,6 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                }
        }
 
-       fws->fifo_credit_map |= 1 << fifo;
        fws->fifo_credit[fifo] += credits;
 }
 
@@ -1042,27 +1127,6 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
                queue_work(fws->fws_wq, &fws->fws_dequeue_work);
 }
 
-static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo,
-                                    struct sk_buff *p)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac;
-
-       if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT)
-                       return;
-               brcmf_fws_return_credits(fws, fifo, 1);
-       } else {
-               /*
-                * if this packet did not count against FIFO credit, it
-                * must have taken a requested_credit from the destination
-                * entry (for pspoll etc.)
-                */
-               if (!brcmf_skb_if_flags_get_field(p, REQUESTED))
-                       entry->requested_credit++;
-       }
-       brcmf_fws_schedule_deq(fws);
-}
-
 static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                         enum brcmf_fws_skb_state state, int fifo,
                         struct sk_buff *p)
@@ -1078,7 +1142,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                return -ENOENT;
        }
 
-       brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len);
+       brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
        if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
                prec += 1;
                qfull_stat = &fws->stats.supprq_full_error;
@@ -1095,14 +1159,12 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
 
        /* update the sk_buff state */
        brcmf_skbcb(p)->state = state;
-       if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
-               entry->suppress_count++;
 
        /*
         * A packet has been pushed so update traffic
         * availability bitmap, if applicable
         */
-       brcmf_fws_tim_update(fws, entry, fifo);
+       brcmf_fws_tim_update(fws, entry, fifo, true);
        brcmf_fws_flow_control_check(fws, &entry->psq,
                                     brcmf_skb_if_flags_get_field(p, INDEX));
        return 0;
@@ -1113,7 +1175,6 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        struct brcmf_fws_mac_descriptor *table;
        struct brcmf_fws_mac_descriptor *entry;
        struct sk_buff *p;
-       int use_credit = 1;
        int num_nodes;
        int node_pos;
        int prec_out;
@@ -1127,7 +1188,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        for (i = 0; i < num_nodes; i++) {
                entry = &table[(node_pos + i) % num_nodes];
                if (!entry->occupied ||
-                   brcmf_fws_mac_desc_closed(fws, entry, fifo))
+                   brcmf_fws_macdesc_closed(fws, entry, fifo))
                        continue;
 
                if (entry->suppressed)
@@ -1137,9 +1198,8 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
                if (p == NULL) {
                        if (entry->suppressed) {
-                               if (entry->suppr_transit_count >
-                                   entry->suppress_count)
-                                       return NULL;
+                               if (entry->suppr_transit_count)
+                                       continue;
                                entry->suppressed = false;
                                p = brcmu_pktq_mdeq(&entry->psq,
                                                    1 << (fifo * 2), &prec_out);
@@ -1148,26 +1208,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                if  (p == NULL)
                        continue;
 
-               /* did the packet come from suppress sub-queue? */
-               if (entry->requested_credit > 0) {
-                       entry->requested_credit--;
-                       /*
-                        * if the packet was pulled out while destination is in
-                        * closed state but had a non-zero packets requested,
-                        * then this should not count against the FIFO credit.
-                        * That is due to the fact that the firmware will
-                        * most likely hold onto this packet until a suitable
-                        * time later to push it to the appropriate AC FIFO.
-                        */
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               } else if (entry->requested_packet > 0) {
-                       entry->requested_packet--;
-                       brcmf_skb_if_flags_set_field(p, REQUESTED, 1);
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               }
-               brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit);
+               brcmf_fws_macdesc_use_req_credit(entry, p);
 
                /* move dequeue position to ensure fair round-robin */
                fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
@@ -1179,7 +1220,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                 * A packet has been picked up, update traffic
                 * availability bitmap, if applicable
                 */
-               brcmf_fws_tim_update(fws, entry, fifo);
+               brcmf_fws_tim_update(fws, entry, fifo, false);
 
                /*
                 * decrement total enqueued fifo packets and
@@ -1192,7 +1233,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        }
        p = NULL;
 done:
-       brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p);
+       brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
        return p;
 }
 
@@ -1202,22 +1243,26 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        u32 hslot;
        int ret;
+       u8 ifidx;
 
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
        /* this packet was suppressed */
-       if (!entry->suppressed || entry->generation != genbit) {
+       if (!entry->suppressed) {
                entry->suppressed = true;
-               entry->suppress_count = brcmu_pktq_mlen(&entry->psq,
-                                                       1 << (fifo * 2 + 1));
                entry->suppr_transit_count = entry->transit_count;
+               brcmf_dbg(DATA, "suppress %s: transit %d\n",
+                         entry->name, entry->transit_count);
        }
 
        entry->generation = genbit;
 
-       ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
+       ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
+       if (ret == 0)
+               ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
+                                   skb);
        if (ret != 0) {
-               /* suppress q is full, drop this packet */
+               /* suppress q is full or hdrpull failed, drop this packet */
                brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
                                        true);
        } else {
@@ -1225,26 +1270,24 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
                 * Mark suppressed to avoid a double free during
                 * wlfc cleanup
                 */
-               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot,
-                                                genbit);
-               entry->suppress_count++;
+               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
        }
 
        return ret;
 }
 
 static int
-brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                           u32 genbit)
 {
        u32 fifo;
        int ret;
        bool remove_from_hanger = true;
        struct sk_buff *skb;
+       struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
 
-       brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n",
-                 flags, hslot);
+       brcmf_dbg(DATA, "flags %d\n", flags);
 
        if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
                fws->stats.txs_discard++;
@@ -1256,6 +1299,8 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                remove_from_hanger = false;
        } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
                fws->stats.txs_tossed++;
+       else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
+               fws->stats.txs_host_tossed++;
        else
                brcmf_err("unexpected txstatus\n");
 
@@ -1266,26 +1311,35 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                return ret;
        }
 
-       entry = brcmf_skbcb(skb)->mac;
+       skcb = brcmf_skbcb(skb);
+       entry = skcb->mac;
        if (WARN_ON(!entry)) {
                brcmu_pkt_buf_free_skb(skb);
                return -EINVAL;
        }
+       entry->transit_count--;
+       if (entry->suppressed && entry->suppr_transit_count)
+               entry->suppr_transit_count--;
+
+       brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags,
+                 skcb->htod);
 
        /* pick up the implicit credit from this packet */
        fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
-       brcmf_skb_pick_up_credit(fws, fifo, skb);
+       if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
+           (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+           (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_schedule_deq(fws);
+       }
+       brcmf_fws_macdesc_return_req_credit(skb);
 
        if (!remove_from_hanger)
                ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit);
 
-       if (remove_from_hanger || ret) {
-               entry->transit_count--;
-               if (entry->suppressed)
-                       entry->suppr_transit_count--;
-
+       if (remove_from_hanger || ret)
                brcmf_txfinalize(fws->drvr, skb, true);
-       }
+
        return 0;
 }
 
@@ -1299,11 +1353,11 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
                return BRCMF_FWS_RET_OK_NOSCHEDULE;
        }
 
-       brcmf_dbg(TRACE, "enter: data %pM\n", data);
+       brcmf_dbg(DATA, "enter: data %pM\n", data);
        for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
                brcmf_fws_return_credits(fws, i, data[i]);
 
-       brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map,
+       brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
                  fws->fifo_delay_map);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
@@ -1323,7 +1377,7 @@ 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);
 
-       return brcmf_fws_txstatus_process(fws, flags, hslot, genbit);
+       return brcmf_fws_txs_process(fws, flags, hslot, genbit);
 }
 
 static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
@@ -1331,7 +1385,7 @@ static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
        __le32 timestamp;
 
        memcpy(&timestamp, &data[2], sizeof(timestamp));
-       brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
+       brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
                  le32_to_cpu(timestamp));
        return 0;
 }
@@ -1364,6 +1418,10 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                brcmf_err("event payload too small (%d)\n", e->datalen);
                return -EINVAL;
        }
+       if (fws->creditmap_received)
+               return 0;
+
+       fws->creditmap_received = true;
 
        brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
        brcmf_fws_lock(ifp->drvr, flags);
@@ -1379,6 +1437,20 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
        return 0;
 }
 
+static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
+                                               const struct brcmf_event_msg *e,
+                                               void *data)
+{
+       struct brcmf_fws_info *fws = ifp->drvr->fws;
+       ulong flags;
+
+       brcmf_fws_lock(ifp->drvr, flags);
+       if (fws)
+               fws->bcmc_credit_check = true;
+       brcmf_fws_unlock(ifp->drvr, flags);
+       return 0;
+}
+
 int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                      struct sk_buff *skb)
 {
@@ -1392,7 +1464,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        s32 status;
        s32 err;
 
-       brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
+       brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
                  ifidx, skb->len, signal_len);
 
        WARN_ON(signal_len > skb->len);
@@ -1426,14 +1498,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                len = signal_data[1];
                data = signal_data + 2;
 
-               brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type,
-                         brcmf_fws_get_tlv_name(type), len, *data);
+               brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
+                         brcmf_fws_get_tlv_name(type), type, len,
+                         brcmf_fws_get_tlv_len(fws, type));
 
                /* abort parsing when length invalid */
                if (data_len < len + 2)
                        break;
 
-               if (len != brcmf_fws_get_tlv_len(fws, type))
+               if (len < brcmf_fws_get_tlv_len(fws, type))
                        break;
 
                err = BRCMF_FWS_RET_OK_NOSCHEDULE;
@@ -1502,64 +1575,32 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        return 0;
 }
 
-static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-       u8 *wlh;
-       u16 data_offset = 0;
-       u8 fillers;
-       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
-
-       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n",
-                 entry->ea, entry->interface_id, le32_to_cpu(pkttag));
-       if (entry->send_tim_signal)
-               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-
-       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
-       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
-       fillers = round_up(data_offset, 4) - data_offset;
-       data_offset += fillers;
-
-       skb_push(skb, data_offset);
-       wlh = skb->data;
-
-       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
-       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
-       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
-       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
-
-       if (entry->send_tim_signal) {
-               entry->send_tim_signal = 0;
-               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
-               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-               wlh[2] = entry->mac_handle;
-               wlh[3] = entry->traffic_pending_bmp;
-               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
-               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
-       }
-       if (fillers)
-               memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
-
-       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
-                           data_offset >> 2, skb);
-       return 0;
-}
-
 static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                                   struct sk_buff *p)
 {
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
        struct brcmf_fws_mac_descriptor *entry = skcb->mac;
        int rc = 0;
-       bool header_needed;
+       bool first_time;
        int hslot = BRCMF_FWS_HANGER_MAXITEMS;
        u8 free_ctr;
-       u8 ifidx;
        u8 flags;
 
-       header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
+       first_time = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
 
-       if (header_needed) {
+       brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
+       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+       brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
+       flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
+       if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
+               /*
+                * Indicate that this packet is being sent in response to an
+                * explicit request from the firmware side.
+                */
+               flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
+       }
+       brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
+       if (first_time) {
                /* obtaining free slot may fail, but that will be caught
                 * by the hanger push. This assures the packet has a BDC
                 * header upon return.
@@ -1568,47 +1609,20 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                free_ctr = entry->seq[fifo];
                brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
                brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr);
-               brcmf_skb_htod_tag_set_field(p, GENERATION, 1);
-               entry->transit_count++;
-       }
-       brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
-       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
-
-       flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
-       if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) {
-               /*
-               Indicate that this packet is being sent in response to an
-               explicit request from the firmware side.
-               */
-               flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
-       }
-       brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
-       if (header_needed) {
-               brcmf_fws_hdrpush(fws, p);
                rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
                if (rc)
                        brcmf_err("hanger push failed: rc=%d\n", rc);
-       } else {
-               int gen;
-
-               /* remove old header */
-               rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p);
-               if (rc == 0) {
-                       hslot = brcmf_skb_htod_tag_get_field(p, HSLOT);
-                       brcmf_fws_hanger_get_genbit(&fws->hanger, p,
-                                                   hslot, &gen);
-                       brcmf_skb_htod_tag_set_field(p, GENERATION, gen);
-
-                       /* push new header */
-                       brcmf_fws_hdrpush(fws, p);
-               }
        }
 
+       if (rc == 0)
+               brcmf_fws_hdrpush(fws, p);
+
        return rc;
 }
 
 static void
-brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
+brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
+                      struct sk_buff *skb, int fifo)
 {
        /*
        put the packet back to the head of queue
@@ -1622,13 +1636,11 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
        enum brcmf_fws_skb_state state;
        struct sk_buff *pktout;
        int rc = 0;
-       int fifo;
        int hslot;
-       u8 ifidx;
 
-       fifo = brcmf_skb_if_flags_get_field(skb, FIFO);
        state = brcmf_skbcb(skb)->state;
        entry = brcmf_skbcb(skb)->mac;
+       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
        if (entry != NULL) {
                if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
@@ -1640,19 +1652,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
                                rc = -ENOSPC;
                        }
                } else {
-                       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
-
-                       /* remove header first */
-                       rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-                       if (rc) {
-                               brcmf_err("header removal failed\n");
-                               /* free the hanger slot */
-                               brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
-                                                       &pktout, true);
-                               rc = -EINVAL;
-                               goto fail;
-                       }
-
                        /* delay-q packets are going to delay-q */
                        pktout = brcmu_pktq_penq_head(&entry->psq,
                                                      2 * fifo, skb);
@@ -1668,33 +1667,30 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
                        /* decrement sequence count */
                        entry->seq[fifo]--;
                }
-               /*
-               if this packet did not count against FIFO credit, it must have
-               taken a requested_credit from the firmware (for pspoll etc.)
-               */
-               if (!(brcmf_skbcb(skb)->if_flags &
-                     BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK))
-                       entry->requested_credit++;
        } else {
                brcmf_err("no mac entry linked\n");
                rc = -ENOENT;
        }
 
-
-fail:
        if (rc) {
-               brcmf_txfinalize(fws->drvr, skb, false);
                fws->stats.rollback_failed++;
-       } else
+               brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
+                                     hslot, 0);
+       } else {
                fws->stats.rollback_success++;
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_macdesc_return_req_credit(skb);
+       }
 }
 
 static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
 {
        int lender_ac;
 
-       if (time_after(fws->borrow_defer_timestamp, jiffies))
+       if (time_after(fws->borrow_defer_timestamp, jiffies)) {
+               fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
                return -ENAVAIL;
+       }
 
        for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
                if (fws->fifo_credit[lender_ac]) {
@@ -1702,10 +1698,12 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
                        fws->fifo_credit[lender_ac]--;
                        if (fws->fifo_credit[lender_ac] == 0)
                                fws->fifo_credit_map &= ~(1 << lender_ac);
-                       brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac);
+                       fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
+                       brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
                        return 0;
                }
        }
+       fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
        return -ENAVAIL;
 }
 
@@ -1714,33 +1712,6 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
 {
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        int *credit = &fws->fifo_credit[fifo];
-       int use_credit = 1;
-
-       brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit);
-
-       if (entry->requested_credit > 0) {
-               /*
-                * if the packet was pulled out while destination is in
-                * closed state but had a non-zero packets requested,
-                * then this should not count against the FIFO credit.
-                * That is due to the fact that the firmware will
-                * most likely hold onto this packet until a suitable
-                * time later to push it to the appropriate AC FIFO.
-                */
-               entry->requested_credit--;
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       } else if (entry->requested_packet > 0) {
-               entry->requested_packet--;
-               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       }
-       brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit);
-       if (!use_credit) {
-               brcmf_dbg(TRACE, "exit: no creditcheck set\n");
-               return 0;
-       }
 
        if (fifo != BRCMF_FWS_FIFO_AC_BE)
                fws->borrow_defer_timestamp = jiffies +
@@ -1748,17 +1719,22 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
 
        if (!(*credit)) {
                /* Try to borrow a credit from other queue */
-               if (fifo == BRCMF_FWS_FIFO_AC_BE &&
-                   brcmf_fws_borrow_credit(fws) == 0)
-                       return 0;
-
-               brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo);
-               return -ENAVAIL;
+               if (fifo != BRCMF_FWS_FIFO_AC_BE ||
+                   (brcmf_fws_borrow_credit(fws) != 0)) {
+                       brcmf_dbg(DATA, "ac=%d, credits depleted\n", fifo);
+                       return -ENAVAIL;
+               }
+       } else {
+               (*credit)--;
+               if (!(*credit))
+                       fws->fifo_credit_map &= ~(1 << fifo);
        }
-       (*credit)--;
-       if (!(*credit))
-               fws->fifo_credit_map &= ~(1 << fifo);
-       brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit);
+
+       brcmf_fws_macdesc_use_req_credit(entry, skb);
+
+       brcmf_dbg(DATA, "ac=%d, credits=%02d:%02d:%02d:%02d\n", fifo,
+                 fws->fifo_credit[0], fws->fifo_credit[1],
+                 fws->fifo_credit[2], fws->fifo_credit[3]);
        return 0;
 }
 
@@ -1769,6 +1745,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry;
        struct brcmf_bus *bus = fws->drvr->bus_if;
        int rc;
+       u8 ifidx;
 
        entry = skcb->mac;
        if (IS_ERR(entry))
@@ -1780,21 +1757,27 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
                goto rollback;
        }
 
+       brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags,
+                 skcb->htod);
        rc = brcmf_bus_txdata(bus, skb);
-       if (rc < 0)
+       if (rc < 0) {
+               brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
                goto rollback;
+       }
 
+       entry->transit_count++;
+       if (entry->suppressed)
+               entry->suppr_transit_count++;
        entry->seq[fifo]++;
        fws->stats.pkt2bus++;
-       if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               fws->stats.send_pkts[fifo]++;
-               fws->stats.fifo_credits_sent[fifo]++;
-       }
+       fws->stats.send_pkts[fifo]++;
+       if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
+               fws->stats.requested_sent[fifo]++;
 
        return rc;
 
 rollback:
-       brcmf_fws_rollback_toq(fws, skb);
+       brcmf_fws_rollback_toq(fws, skb, fifo);
        return rc;
 }
 
@@ -1826,19 +1809,25 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
 
        /* set control buffer information */
        skcb->if_flags = 0;
-       skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest);
+       skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
        skcb->state = BRCMF_FWS_SKBSTATE_NEW;
        brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
        if (!multicast)
                fifo = brcmf_fws_prio2fifo[skb->priority];
-       brcmf_skb_if_flags_set_field(skb, FIFO, fifo);
 
-       brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest,
-                 multicast, fifo);
+       brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
+                 eh->h_dest, multicast, fifo);
 
        brcmf_fws_lock(drvr, flags);
+       /* multicast credit support is conditional, setting
+        * flag to false to assure credit is consumed below.
+        */
+       if (fws->bcmc_credit_check)
+               multicast = false;
+
        if (skcb->mac->suppressed ||
-           brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) ||
+           fws->bus_flow_blocked ||
+           brcmf_fws_macdesc_closed(fws, skcb->mac, fifo) ||
            brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) ||
            (!multicast &&
             brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
@@ -1846,9 +1835,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                drvr->fws->fifo_delay_map |= 1 << fifo;
                brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
        } else {
-               if (brcmf_fws_commit_skb(fws, fifo, skb))
-                       if (!multicast)
-                               brcmf_skb_pick_up_credit(fws, fifo, skb);
+               brcmf_fws_commit_skb(fws, fifo, skb);
        }
        brcmf_fws_unlock(drvr, flags);
        return 0;
@@ -1862,7 +1849,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp)
        if (!entry)
                return;
 
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
 }
 
 void brcmf_fws_add_interface(struct brcmf_if *ifp)
@@ -1870,16 +1857,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
        struct brcmf_fws_info *fws = ifp->drvr->fws;
        struct brcmf_fws_mac_descriptor *entry;
 
-       brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
-                 ifp->bssidx, ifp->mac_addr);
        if (!ifp->ndev || !ifp->drvr->fw_signals)
                return;
 
        entry = &fws->desc.iface[ifp->ifidx];
        ifp->fws_desc = entry;
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_set_name(fws, entry);
        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
+       brcmf_dbg(TRACE, "added %s\n", entry->name);
 }
 
 void brcmf_fws_del_interface(struct brcmf_if *ifp)
@@ -1887,13 +1874,13 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
        struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
        ulong flags;
 
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
        if (!entry)
                return;
 
        brcmf_fws_lock(ifp->drvr, flags);
        ifp->fws_desc = NULL;
-       brcmf_fws_clear_mac_descriptor(entry);
+       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);
 }
@@ -1904,39 +1891,37 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
        struct sk_buff *skb;
        ulong flags;
        int fifo;
-       int credit;
 
        fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
 
-       brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
        brcmf_fws_lock(fws->drvr, flags);
-       for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) {
-               brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
-                         fws->fifo_credit[fifo]);
-               for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
+       for (fifo = NL80211_NUM_ACS; fifo >= 0 && !fws->bus_flow_blocked;
+            fifo--) {
+               while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
+                      (fifo == BRCMF_FWS_FIFO_BCMC))) {
                        skb = brcmf_fws_deq(fws, fifo);
-                       if (!skb || brcmf_fws_commit_skb(fws, fifo, skb))
+                       if (!skb)
+                               break;
+                       fws->fifo_credit[fifo]--;
+                       if (brcmf_fws_commit_skb(fws, fifo, skb))
+                               break;
+                       if (fws->bus_flow_blocked)
                                break;
-                       if (brcmf_skbcb(skb)->if_flags &
-                           BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
-                               credit++;
                }
                if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
-                   (credit == fws->fifo_credit[fifo])) {
-                       fws->fifo_credit[fifo] -= credit;
+                   (fws->fifo_credit[fifo] == 0) &&
+                   (!fws->bus_flow_blocked)) {
                        while (brcmf_fws_borrow_credit(fws) == 0) {
                                skb = brcmf_fws_deq(fws, fifo);
                                if (!skb) {
                                        brcmf_fws_return_credits(fws, fifo, 1);
                                        break;
                                }
-                               if (brcmf_fws_commit_skb(fws, fifo, skb)) {
-                                       brcmf_fws_return_credits(fws, fifo, 1);
+                               if (brcmf_fws_commit_skb(fws, fifo, skb))
+                                       break;
+                               if (fws->bus_flow_blocked)
                                        break;
-                               }
                        }
-               } else {
-                       fws->fifo_credit[fifo] -= credit;
                }
        }
        brcmf_fws_unlock(fws->drvr, flags);
@@ -1982,6 +1967,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                brcmf_err("register credit map handler failed\n");
                goto fail;
        }
+       rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
+                                brcmf_fws_notify_bcmc_credit_support);
+       if (rc < 0) {
+               brcmf_err("register bcmc credit handler failed\n");
+               brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
+               goto fail;
+       }
 
        /* setting the iovar may fail if feature is unsupported
         * so leave the rc as is so driver initialization can
@@ -1993,19 +1985,20 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
        }
 
        brcmf_fws_hanger_init(&drvr->fws->hanger);
-       brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0);
+       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,
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
        brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
 
-       /* TODO: remove upon feature delivery */
-       brcmf_err("%s bdcv2 tlv signaling [%x]\n",
+       brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
                  drvr->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);
@@ -2043,25 +2036,31 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
        if (!fws)
                return false;
 
-       brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode);
        return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
 }
 
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
        ulong flags;
+       u32 hslot;
 
-       brcmf_fws_lock(fws->drvr, flags);
-       brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED,
-                                  brcmf_skb_htod_tag_get_field(skb, HSLOT), 0);
-       /* the packet never reached firmware so reclaim credit */
-       if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT &&
-           brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               brcmf_fws_return_credits(fws,
-                                        brcmf_skb_htod_tag_get_field(skb,
-                                                                     FIFO),
-                                        1);
-               brcmf_fws_schedule_deq(fws);
+       if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+               brcmu_pkt_buf_free_skb(skb);
+               return;
        }
+       brcmf_fws_lock(fws->drvr, flags);
+       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);
 }
+
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
+{
+       struct brcmf_fws_info *fws = drvr->fws;
+
+       fws->bus_flow_blocked = flow_blocked;
+       if (!flow_blocked)
+               brcmf_fws_schedule_deq(fws);
+       else
+               fws->stats.bus_flow_block++;
+}
index fbe483d..9fc8609 100644 (file)
@@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
 void brcmf_fws_add_interface(struct brcmf_if *ifp);
 void brcmf_fws_del_interface(struct brcmf_if *ifp);
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
 
 #endif /* FWSIGNAL_H_ */
index 7c1b633..793df66 100644 (file)
@@ -170,7 +170,6 @@ struct brcmf_sdio_dev {
        atomic_t suspend;               /* suspend flag */
        wait_queue_head_t request_byte_wait;
        wait_queue_head_t request_word_wait;
-       wait_queue_head_t request_chain_wait;
        wait_queue_head_t request_buffer_wait;
        struct device *dev;
        struct brcmf_bus *bus_if;
@@ -272,16 +271,6 @@ brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
                         uint rw, uint fnc, uint addr,
                         u32 *word, uint nbyte);
 
-/* read or write any buffer using cmd53 */
-extern int
-brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                          uint fix_inc, uint rw, uint fnc_num, u32 addr,
-                          struct sk_buff *pkt);
-extern int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq);
-
 /* Watchdog timer interface for pm ops */
 extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev,
                                    bool enable);
@@ -291,4 +280,8 @@ extern void brcmf_sdbrcm_disconnect(void *ptr);
 extern void brcmf_sdbrcm_isr(void *arg);
 
 extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+
+extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
+                                wait_queue_head_t *wq);
+extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
 #endif                         /* _BRCM_SDH_H_ */
index 9df1f7a..bc29171 100644 (file)
@@ -87,6 +87,27 @@ TRACE_EVENT(brcmf_hexdump,
        TP_printk("hexdump [length=%lu]", __entry->len)
 );
 
+TRACE_EVENT(brcmf_bdchdr,
+       TP_PROTO(void *data),
+       TP_ARGS(data),
+       TP_STRUCT__entry(
+               __field(u8, flags)
+               __field(u8, prio)
+               __field(u8, flags2)
+               __field(u32, siglen)
+               __dynamic_array(u8, signal, *((u8 *)data + 3) * 4)
+       ),
+       TP_fast_assign(
+               __entry->flags = *(u8 *)data;
+               __entry->prio = *((u8 *)data + 1);
+               __entry->flags2 = *((u8 *)data + 2);
+               __entry->siglen = *((u8 *)data + 3) * 4;
+               memcpy(__get_dynamic_array(signal),
+                      (u8 *)data + 4, __entry->siglen);
+       ),
+       TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
+);
+
 #ifdef CONFIG_BRCM_TRACING
 
 #undef TRACE_INCLUDE_PATH
index 01aed7a..322cadc 100644 (file)
@@ -82,6 +82,7 @@ struct brcmf_usbdev_info {
        int tx_high_watermark;
        int tx_freecount;
        bool tx_flowblock;
+       spinlock_t tx_flowblock_lock;
 
        struct brcmf_usbreq *tx_reqs;
        struct brcmf_usbreq *rx_reqs;
@@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
 {
        struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
        struct brcmf_usbdev_info *devinfo = req->devinfo;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
                  req->skb);
@@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb)
        brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
        req->skb = NULL;
        brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
                devinfo->tx_flowblock) {
                brcmf_txflowblock(devinfo->dev, false);
                devinfo->tx_flowblock = false;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
 }
 
 static void brcmf_usb_rx_complete(struct urb *urb)
@@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
        struct brcmf_usbreq  *req;
        int ret;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, skb=%p\n", skb);
        if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
@@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
                goto fail;
        }
 
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
            !devinfo->tx_flowblock) {
                brcmf_txflowblock(dev, true);
                devinfo->tx_flowblock = true;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
        return 0;
 
 fail:
@@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
 
        /* Initialize the spinlocks */
        spin_lock_init(&devinfo->qlock);
+       spin_lock_init(&devinfo->tx_flowblock_lock);
 
        INIT_LIST_HEAD(&devinfo->rx_freeq);
        INIT_LIST_HEAD(&devinfo->rx_postq);
index 301e572..277b37a 100644 (file)
@@ -3982,6 +3982,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct brcmf_fil_af_params_le *af_params;
        bool ack;
        s32 chan_nr;
+       u32 freq;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -3994,6 +3995,8 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EPERM;
        }
 
+       vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
        if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* Right now the only reason to get a probe response */
                /* is for p2p listen response or for p2p GO from     */
@@ -4009,7 +4012,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                ie_offset =  DOT11_MGMT_HDR_LEN +
                             DOT11_BCN_PRB_FIXED_LEN;
                ie_len = len - ie_offset;
-               vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
                if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
                        vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
                err = brcmf_vif_set_mgmt_ie(vif,
@@ -4033,16 +4035,22 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
                /* Add the length exepted for 802.11 header  */
                action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
-               /* Add the channel */
-               chan_nr = ieee80211_frequency_to_channel(chan->center_freq);
+               /* Add the channel. Use the one specified as parameter if any or
+                * the current one (got from the firmware) otherwise
+                */
+               if (chan)
+                       freq = chan->center_freq;
+               else
+                       brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+                                             &freq);
+               chan_nr = ieee80211_frequency_to_channel(freq);
                af_params->channel = cpu_to_le32(chan_nr);
 
                memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
                       le16_to_cpu(action_frame->len));
 
                brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
-                         *cookie, le16_to_cpu(action_frame->len),
-                         chan->center_freq);
+                         *cookie, le16_to_cpu(action_frame->len), freq);
 
                ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
                                                  af_params);
index 9f9adb4..da88503 100644 (file)
@@ -245,6 +245,14 @@ module_param(cw1200_ba_tx_tids, int, 0644);
 MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
 MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support cw1200_wowlan_support = {
+       /* Support only for limited wowlan functionalities */
+       .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
+
 static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
                                                const bool have_5ghz)
 {
@@ -289,10 +297,7 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
                                          BIT(NL80211_IFTYPE_P2P_GO);
 
 #ifdef CONFIG_PM
-       /* Support only for limited wowlan functionalities */
-       hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
-               WIPHY_WOWLAN_DISCONNECT;
-       hw->wiphy->wowlan.n_patterns = 0;
+       hw->wiphy->wowlan = &cw1200_wowlan_support;
 #endif
 
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
index dce5e8f..9581d07 100644 (file)
@@ -3727,7 +3727,8 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * 5. Setup HW Constants
         * ********************/
        /* Device-specific setup */
-       if (il3945_hw_set_hw_params(il)) {
+       err = il3945_hw_set_hw_params(il);
+       if (err) {
                IL_ERR("failed to set hw settings\n");
                goto out_eeprom_free;
        }
index dc1e6da..c092033 100644 (file)
@@ -331,6 +331,19 @@ il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        txq->time_stamp = jiffies;
        info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]);
        ieee80211_tx_info_clear_status(info);
@@ -488,6 +501,11 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        skb = dev_alloc_skb(128);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
index 3c4899b..b9b2bb5 100644 (file)
@@ -588,6 +588,11 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        /* In case of HW accelerated crypto and bad decryption, drop */
        if (!il->cfg->mod_params->sw_crypto &&
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
@@ -2806,6 +2811,19 @@ il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        spin_lock_irqsave(&il->sta_lock, flags);
        if (txq->sched_retry) {
                const u32 scd_ssn = il4965_get_scd_ssn(tx_resp);
@@ -5741,7 +5759,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
        hw->flags =
            IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION |
            IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT |
-           IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+           IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
        if (il->cfg->sku & IL_SKU_N)
                hw->flags |=
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
index e9a3cbc..3195aad 100644 (file)
@@ -5306,6 +5306,17 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (changes & BSS_CHANGED_BSSID) {
                D_MAC80211("BSSID %pM\n", bss_conf->bssid);
 
+               /*
+                * On passive channel we wait with blocked queues to see if
+                * there is traffic on that channel. If no frame will be
+                * received (what is very unlikely since scan detects AP on
+                * that channel, but theoretically possible), mac80211 associate
+                * procedure will time out and mac80211 will call us with NULL
+                * bssid. We have to unblock queues on such condition.
+                */
+               if (is_zero_ether_addr(bss_conf->bssid))
+                       il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+
                /*
                 * If there is currently a HW scan going on in the background,
                 * then we need to cancel it, otherwise sometimes we are not
index 4caaf52..83f8ed8 100644 (file)
@@ -1299,6 +1299,8 @@ struct il_priv {
        /* queue refcounts */
 #define IL_MAX_HW_QUEUES       32
        unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)];
+#define IL_STOP_REASON_PASSIVE 0
+       unsigned long stop_reason;
        /* for each AC */
        atomic_t queue_stop_count[4];
 
@@ -2256,6 +2258,19 @@ il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq)
        txq->swq_id = (hwq << 2) | ac;
 }
 
+static inline void
+_il_wake_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
+               ieee80211_wake_queue(il->hw, ac);
+}
+
+static inline void
+_il_stop_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
+               ieee80211_stop_queue(il->hw, ac);
+}
 static inline void
 il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
 {
@@ -2264,8 +2279,7 @@ il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (test_and_clear_bit(hwq, il->queue_stopped))
-               if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
-                       ieee80211_wake_queue(il->hw, ac);
+               _il_wake_queue(il, ac);
 }
 
 static inline void
@@ -2276,8 +2290,27 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (!test_and_set_bit(hwq, il->queue_stopped))
-               if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
-                       ieee80211_stop_queue(il->hw, ac);
+               _il_stop_queue(il, ac);
+}
+
+static inline void
+il_wake_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (test_and_clear_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_wake_queue(il, ac);
+}
+
+static inline void
+il_stop_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (!test_and_set_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_stop_queue(il, ac);
 }
 
 #ifdef ieee80211_stop_queue
index 3b5613e..f55a758 100644 (file)
@@ -7,14 +7,16 @@ iwlwifi-objs          += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
 iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
-iwlwifi-objs           += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o
+iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o
+
+iwlwifi-objs += $(iwlwifi-m)
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
 
-
 obj-$(CONFIG_IWLDVM)   += dvm/
 obj-$(CONFIG_IWLMVM)   += mvm/
 
index f1b8df1..5cd87f9 100644 (file)
@@ -915,6 +915,9 @@ struct iwl_priv {
        __le64 replay_ctr;
        __le16 last_seq_ctl;
        bool have_rekey_data;
+#ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan_support;
+#endif
 
        /* device_pointers: pointers to ucode event tables */
        struct {
index c0039a9..eef64bb 100644 (file)
@@ -208,20 +208,21 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
            priv->trans->ops->d3_suspend &&
            priv->trans->ops->d3_resume &&
            device_can_wakeup(priv->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                            WIPHY_WOWLAN_DISCONNECT |
+                                            WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                            WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
+                       priv->wowlan_support.flags |=
                                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                WIPHY_WOWLAN_GTK_REKEY_FAILURE;
 
-               hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len =
+               priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
+               priv->wowlan_support.pattern_min_len =
                                        IWLAGN_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len =
+               priv->wowlan_support.pattern_max_len =
                                        IWLAGN_WOWLAN_MAX_PATTERN_LEN;
+               hw->wiphy->wowlan = &priv->wowlan_support;
        }
 #endif
 
index 68f7546..7aa9c8d 100644 (file)
@@ -1859,14 +1859,9 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
                return pos;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
                size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
                        ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
-       size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
-               ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
        IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
                size);
 
@@ -1910,10 +1905,8 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
        unsigned int reload_msec;
        unsigned long reload_jiffies;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_FW_ERRORS))
                iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
-#endif
 
        /* uCode is no longer loaded. */
        priv->ucode_loaded = false;
index a193832..0189b90 100644 (file)
@@ -237,6 +237,7 @@ struct iwl_cfg {
 /*
  * This list declares the config structures for all devices.
  */
+#if IS_ENABLED(CONFIG_IWLDVM)
 extern const struct iwl_cfg iwl5300_agn_cfg;
 extern const struct iwl_cfg iwl5100_agn_cfg;
 extern const struct iwl_cfg iwl5350_agn_cfg;
@@ -278,11 +279,14 @@ extern const struct iwl_cfg iwl6035_2agn_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 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_2n_cfg;
 extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
 extern const struct iwl_cfg iwl3160_2n_cfg;
 extern const struct iwl_cfg iwl3160_n_cfg;
+#endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 8cf5db7..7edb851 100644 (file)
 
 static inline bool iwl_have_debug_level(u32 level)
 {
+#ifdef CONFIG_IWLWIFI_DEBUG
        return iwlwifi_mod_params.debug_level & level;
+#else
+       return false;
+#endif
 }
 
 void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace,
index 7d14509..429337a 100644 (file)
@@ -62,8 +62,7 @@
 
 #ifndef __iwl_drv_h__
 #define __iwl_drv_h__
-
-#include <linux/module.h>
+#include <linux/export.h>
 
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
index 36dfe09..d4ad505 100644 (file)
@@ -115,7 +115,9 @@ struct iwl_mod_params {
        int led_mode;
        bool power_save;
        int power_level;
+#ifdef CONFIG_IWLWIFI_DEBUG
        u32 debug_level;
+#endif
        int ant_coupling;
        bool bt_ch_announce;
        bool auto_agg;
index 25745da..1a405ae 100644 (file)
@@ -92,20 +92,16 @@ struct iwl_phy_db_entry {
 struct iwl_phy_db {
        struct iwl_phy_db_entry cfg;
        struct iwl_phy_db_entry calib_nch;
-       struct iwl_phy_db_entry calib_ch;
        struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
        struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
 
-       u32 channel_num;
-       u32 channel_size;
-
        struct iwl_trans *trans;
 };
 
 enum iwl_phy_db_section_type {
        IWL_PHY_DB_CFG = 1,
        IWL_PHY_DB_CALIB_NCH,
-       IWL_PHY_DB_CALIB_CH,
+       IWL_PHY_DB_UNUSED,
        IWL_PHY_DB_CALIB_CHG_PAPD,
        IWL_PHY_DB_CALIB_CHG_TXP,
        IWL_PHY_DB_MAX
@@ -169,8 +165,6 @@ iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
                return &phy_db->cfg;
        case IWL_PHY_DB_CALIB_NCH:
                return &phy_db->calib_nch;
-       case IWL_PHY_DB_CALIB_CH:
-               return &phy_db->calib_ch;
        case IWL_PHY_DB_CALIB_CHG_PAPD:
                if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
                        return NULL;
@@ -208,7 +202,6 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db)
 
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
        for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
                iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
        for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
@@ -248,13 +241,6 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
 
        entry->size = size;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               phy_db->channel_num =
-                       le32_to_cpup((__le32 *)phy_db_notif->data);
-               phy_db->channel_size =
-                       (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
-       }
-
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
                       __func__, __LINE__, type, size);
@@ -328,10 +314,7 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
                                u32 type, u8 **data, u16 *size, u16 ch_id)
 {
        struct iwl_phy_db_entry *entry;
-       u32 channel_num;
-       u32 channel_size;
        u16 ch_group_id = 0;
-       u16 index;
 
        if (!phy_db)
                return -EINVAL;
@@ -346,21 +329,8 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
        if (!entry)
                return -EINVAL;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               index = ch_id_to_ch_index(ch_id);
-               channel_num = phy_db->channel_num;
-               channel_size = phy_db->channel_size;
-               if (index >= channel_num) {
-                       IWL_ERR(phy_db->trans, "Wrong channel number %d\n",
-                               ch_id);
-                       return -EINVAL;
-               }
-               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
-               *size = channel_size;
-       } else {
-               *data = entry->data;
-               *size = entry->size;
-       }
+       *data = entry->data;
+       *size = entry->size;
 
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
@@ -413,6 +383,9 @@ static int iwl_phy_db_send_all_channel_groups(
                if (!entry)
                        return -EINVAL;
 
+               if (WARN_ON_ONCE(!entry->size))
+                       continue;
+
                /* Send the requested PHY DB section */
                err = iwl_send_phy_db_cmd(phy_db,
                                          type,
index 7a2ef3f..8c49db0 100644 (file)
@@ -420,8 +420,7 @@ static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
        return cpu_to_le16(be16_to_cpu((__force __be16)check));
 }
 
-static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif,
+static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
                                     struct cfg80211_wowlan_tcp *tcp,
                                     void *_pkt, u8 *mask,
                                     __le16 *pseudo_hdr_csum,
@@ -567,21 +566,21 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* SYN (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->syn_tx.data, NULL,
+               vif, tcp, cfg->syn_tx.data, NULL,
                &cfg->syn_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_SYN);
        cfg->syn_tx.info.tcp_payload_length = 0;
 
        /* SYN/ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
+               vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
                &cfg->synack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_SYNACK);
        cfg->synack_rx.info.tcp_payload_length = 0;
 
        /* KEEPALIVE/ACK (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_tx.data, NULL,
+               vif, tcp, cfg->keepalive_tx.data, NULL,
                &cfg->keepalive_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_DATA);
        cfg->keepalive_tx.info.tcp_payload_length =
@@ -605,7 +604,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_ack_rx.data,
+               vif, tcp, cfg->keepalive_ack_rx.data,
                cfg->keepalive_ack_rx.rx_mask,
                &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_ACK);
@@ -613,7 +612,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* WAKEUP (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
+               vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
                &cfg->wake_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_WAKE);
        cfg->wake_rx.info.tcp_payload_length =
@@ -621,7 +620,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* FIN */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->fin_tx.data, NULL,
+               vif, tcp, cfg->fin_tx.data, NULL,
                &cfg->fin_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_FIN);
        cfg->fin_tx.info.tcp_payload_length = 0;
index 6994232..700cce7 100644 (file)
@@ -228,10 +228,11 @@ struct iwl_tx_cmd {
        __le16 len;
        __le16 next_frame_len;
        __le32 tx_flags;
-       /* DRAM_SCRATCH_API_U_VER_1 */
-       u8 try_cnt;
-       u8 btkill_cnt;
-       __le16 reserved;
+       struct {
+               u8 try_cnt;
+               u8 btkill_cnt;
+               __le16 reserved;
+       } scratch; /* DRAM_SCRATCH_API_U_VER_1 */
        __le32 rate_n_flags;
        u8 sta_id;
        u8 sec_ctl;
index 46c7c05..273b0cc 100644 (file)
@@ -193,14 +193,11 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
 u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif)
 {
-       u32 qmask, ac;
+       u32 qmask = 0, ac;
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
                return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
 
-       qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
-               BIT(vif->cab_queue) : 0;
-
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
                if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
                        qmask |= BIT(vif->hw_queue[ac]);
index 2ed296c..e08683b 100644 (file)
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                       BIT(NL80211_IFTYPE_AP),
+               .types = BIT(NL80211_IFTYPE_STATION),
        },
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               .types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_P2P_GO),
        },
        {
@@ -236,20 +236,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                   WIPHY_WOWLAN_DISCONNECT |
+                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                   WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
-                               WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                               WIPHY_WOWLAN_GTK_REKEY_FAILURE |
-                               WIPHY_WOWLAN_4WAY_HANDSHAKE;
-
-               hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
-               hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+                       mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                                            WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+                                            WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+               mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+               mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+               mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+               hw->wiphy->wowlan = &mvm->wowlan;
        }
 #endif
 
@@ -651,8 +651,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                 * By now, all the AC queues are empty. The AGG queues are
                 * empty too. We already got all the Tx responses for all the
                 * packets in the queues. The drain work can have been
-                * triggered. Flush it. This work item takes the mutex, so kill
-                * it before we take it.
+                * triggered. Flush it.
                 */
                flush_work(&mvm->sta_drained_wk);
        }
@@ -778,7 +777,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                ret = iwl_mvm_power_update_mode(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
-       } else if (changes & BSS_CHANGED_DTIM_PERIOD) {
+       } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
                 * remove the session protection.
index 4e10aae..c7409f1 100644 (file)
@@ -458,6 +458,7 @@ struct iwl_mvm {
        struct ieee80211_vif *p2p_device_vif;
 
 #ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan;
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        bool d3_test_active;
@@ -689,16 +690,11 @@ void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd);
-int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm);
 #else
 static inline void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd)
 {}
-static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm)
-{
-       return 0;
-}
 #endif
 int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
index 2278858..62fe520 100644 (file)
@@ -229,9 +229,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
                        mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
 
-       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
-               mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue);
-
        /* for HW restart - need to reset the seq_number etc... */
        memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
 
@@ -1292,17 +1289,11 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
-               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
-               .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE),
+               .station_flags_msk = cpu_to_le32(STA_FLG_PS),
                .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
        };
        int ret;
 
-       /*
-        * Same modify mask for sleep_tx_count and sleep_state_flags but this
-        * should be fine since if we set the STA as "awake", then
-        * sleep_tx_count is not relevant.
-        */
        ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
index 3efa0a0..94b265e 100644 (file)
@@ -250,7 +250,6 @@ enum iwl_mvm_agg_state {
  *     the first packet to be sent in legacy HW queue in Tx AGG stop flow.
  *     Basically when next_reclaimed reaches ssn, we can tell mac80211 that
  *     we are ready to finish the Tx AGG stop / start flow.
- * @wait_for_ba: Expect block-ack before next Tx reply
  */
 struct iwl_mvm_tid_data {
        u16 seq_number;
@@ -260,7 +259,6 @@ struct iwl_mvm_tid_data {
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
-       bool wait_for_ba;
 };
 
 /**
index b9ba4e7..f0e96a9 100644 (file)
@@ -408,7 +408,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
        IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
                     tid, txq_id, seq_number);
 
-       /* NOTE: aggregation will need changes here (for txq id) */
        if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
                goto drop_unlock_sta;
 
@@ -610,8 +609,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
-               /* W/A FW bug: seq_ctl is wrong when the queue is flushed */
-               if (status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+               /* W/A FW bug: seq_ctl is wrong when the status isn't success */
+               if (status != TX_STATUS_SUCCESS) {
                        struct ieee80211_hdr *hdr = (void *)skb->data;
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
index db7bdd3..81f3ea5 100644 (file)
@@ -78,6 +78,7 @@
 
 /* Hardware specific file defines the PCI IDs table for that hardware module */
 static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
+#if IS_ENABLED(CONFIG_IWLDVM)
        {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
@@ -253,7 +254,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
+#endif /* CONFIG_IWLDVM */
 
+#if IS_ENABLED(CONFIG_IWLMVM)
 /* 7000 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
@@ -304,6 +307,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+#endif /* CONFIG_IWLMVM */
 
        {0}
 };
index 148843e..b654dcd 100644 (file)
@@ -217,6 +217,7 @@ struct iwl_pcie_txq_scratch_buf {
  * @trans_pcie: pointer back to transport (for timer)
  * @need_update: indicates need to update read/write index
  * @active: stores if queue is active
+ * @ampdu: true if this queue is an ampdu queue for an specific RA/TID
  *
  * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
  * descriptors) and required locking structures.
@@ -232,6 +233,7 @@ struct iwl_txq {
        struct iwl_trans_pcie *trans_pcie;
        u8 need_update;
        u8 active;
+       bool ampdu;
 };
 
 static inline dma_addr_t
index 567e67a..3688dc5 100644 (file)
@@ -802,9 +802,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        u32 handled = 0;
        unsigned long flags;
        u32 i;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_mask;
-#endif
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
@@ -826,14 +823,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        inta = trans_pcie->inta;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               /* just for debug */
-               inta_mask = iwl_read32(trans, CSR_INT_MASK);
+       if (iwl_have_debug_level(IWL_DL_ISR))
                IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
-                             inta, inta_mask);
-       }
-#endif
+                             inta, iwl_read32(trans, CSR_INT_MASK));
 
        /* saved interrupt in inta variable now we can reset trans_pcie->inta */
        trans_pcie->inta = 0;
@@ -855,12 +847,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                goto out;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_ISR)) {
                /* NIC fires this, but we don't use it, redundant with WAKEUP */
                if (inta & CSR_INT_BIT_SCD) {
-                       IWL_DEBUG_ISR(trans, "Scheduler finished to transmit "
-                                     "the frame/frames.\n");
+                       IWL_DEBUG_ISR(trans,
+                                     "Scheduler finished to transmit the frame/frames.\n");
                        isr_stats->sch++;
                }
 
@@ -870,7 +861,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                        isr_stats->alive++;
                }
        }
-#endif
+
        /* Safely ignore these bits for debug checks below */
        inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
 
@@ -1118,9 +1109,6 @@ 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;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_fh;
-#endif
 
        lockdep_assert_held(&trans_pcie->irq_lock);
 
@@ -1159,13 +1147,11 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
                return IRQ_HANDLED;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS);
-               IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, "
-                             "fh 0x%08x\n", inta, inta_mask, inta_fh);
-       }
-#endif
+       if (iwl_have_debug_level(IWL_DL_ISR))
+               IWL_DEBUG_ISR(trans,
+                             "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
+                             inta, inta_mask,
+                             iwl_read32(trans, CSR_FH_INT_STATUS));
 
        trans_pcie->inta |= inta;
        /* the thread will service interrupts and re-enable them */
@@ -1198,7 +1184,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
 {
        struct iwl_trans *trans = data;
        struct iwl_trans_pcie *trans_pcie;
-       u32 inta, inta_mask;
+       u32 inta;
        u32 val = 0;
        u32 read;
        unsigned long flags;
@@ -1226,7 +1212,6 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
         * If we have something to service, the tasklet will re-enable ints.
         * If we *don't* have something, we'll re-enable before leaving here.
         */
-       inta_mask = iwl_read32(trans, CSR_INT_MASK);
        iwl_write32(trans, CSR_INT_MASK, 0x00000000);
 
        /* Ignore interrupt if there's nothing in NIC to service.
@@ -1271,8 +1256,11 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
                val |= 0x8000;
 
        inta = (0xff & val) | ((0xff00 & val) << 16);
-       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
-                     inta, inta_mask, val);
+       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled(sw) 0x%08x ict 0x%08x\n",
+                     inta, trans_pcie->inta_mask, val);
+       if (iwl_have_debug_level(IWL_DL_ISR))
+               IWL_DEBUG_ISR(trans, "enabled(hw) 0x%08x\n",
+                             iwl_read32(trans, CSR_INT_MASK));
 
        inta &= trans_pcie->inta_mask;
        trans_pcie->inta |= inta;
index f65da19..c47c921 100644 (file)
@@ -576,10 +576,16 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
 
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
+               IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
+                                  txq_id, q->read_ptr);
                iwl_pcie_txq_free_tfd(trans, txq);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
        }
+       txq->active = false;
        spin_unlock_bh(&txq->lock);
+
+       /* just in case - this queue may have been stopped */
+       iwl_wake_queue(trans, txq);
 }
 
 /*
@@ -927,6 +933,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        spin_lock_bh(&txq->lock);
 
+       if (!txq->active) {
+               IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n",
+                                   txq_id, ssn);
+               goto out;
+       }
+
        if (txq->q.read_ptr == tfd_num)
                goto out;
 
@@ -1073,6 +1085,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
 
                /* enable aggregations for the queue */
                iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+               trans_pcie->txq[txq_id].ampdu = true;
        } else {
                /*
                 * disable aggregations for the queue, this will also make the
@@ -1107,6 +1120,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                       (fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
                       (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
                       SCD_QUEUE_STTS_REG_MSK);
+       trans_pcie->txq[txq_id].active = true;
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
                            txq_id, fifo, ssn & 0xff);
 }
@@ -1129,6 +1143,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
                            ARRAY_SIZE(zero_val));
 
        iwl_pcie_txq_unmap(trans, txq_id);
+       trans_pcie->txq[txq_id].ampdu = false;
 
        IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
 }
@@ -1599,7 +1614,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        u8 wait_write_ptr = 0;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
-       u16 __maybe_unused wifi_seq;
+       u16 wifi_seq;
 
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
@@ -1616,13 +1631,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         * the BA.
         * Check here that the packets are in the right place on the ring.
         */
-#ifdef CONFIG_IWLWIFI_DEBUG
        wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
-       WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) &&
-                 ((wifi_seq & 0xff) != q->write_ptr),
+       WARN_ONCE(trans_pcie->txq[txq_id].ampdu &&
+                 (wifi_seq & 0xff) != q->write_ptr,
                  "Q: %d WiFi Seq %d tfdNum %d",
                  txq_id, wifi_seq, q->write_ptr);
-#endif
 
        /* Set up driver data for this TFD */
        txq->entries[q->write_ptr].skb = skb;
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
new file mode 100644 (file)
index 0000000..8d68307
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11h
+ *
+ * Copyright (C) 2013, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+#include "fw.h"
+
+
+/* This function appends 11h info to a buffer while joining an
+ * infrastructure BSS
+ */
+static void
+mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer,
+                              struct mwifiex_bssdescriptor *bss_desc)
+{
+       struct mwifiex_ie_types_header *ie_header;
+       struct mwifiex_ie_types_pwr_capability *cap;
+       struct mwifiex_ie_types_local_pwr_constraint *constraint;
+       struct ieee80211_supported_band *sband;
+       u8 radio_type;
+       int i;
+
+       if (!buffer || !(*buffer))
+               return;
+
+       radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+       sband = priv->wdev->wiphy->bands[radio_type];
+
+       cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
+       cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
+       cap->header.len = cpu_to_le16(2);
+       cap->min_pwr = 0;
+       cap->max_pwr = 0;
+       *buffer += sizeof(*cap);
+
+       constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer;
+       constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
+       constraint->header.len = cpu_to_le16(2);
+       constraint->chan = bss_desc->channel;
+       constraint->constraint = bss_desc->local_constraint;
+       *buffer += sizeof(*constraint);
+
+       ie_header = (struct mwifiex_ie_types_header *)*buffer;
+       ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+       ie_header->len  = cpu_to_le16(2 * sband->n_channels + 2);
+       *buffer += sizeof(*ie_header);
+       *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
+       *(*buffer)++ = 2 * sband->n_channels;
+       for (i = 0; i < sband->n_channels; i++) {
+               *(*buffer)++ = ieee80211_frequency_to_channel(
+                                       sband->channels[i].center_freq);
+               *(*buffer)++ = 1; /* one channel in the subband */
+       }
+}
+
+/* Enable or disable the 11h extensions in the firmware */
+static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
+{
+       u32 enable = flag;
+
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
+                                    HostCmd_ACT_GEN_SET, DOT11H_I, &enable);
+}
+
+/* This functions processes TLV buffer for a pending BSS Join command.
+ *
+ * Activate 11h functionality in the firmware if the spectrum management
+ * capability bit is found in the network we are joining. Also, necessary
+ * TLVs are set based on requested network's 11h capability.
+ */
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+                             struct mwifiex_bssdescriptor *bss_desc)
+{
+       if (bss_desc->sensed_11h) {
+               /* Activate 11h functions in firmware, turns on capability
+                * bit
+                */
+               mwifiex_11h_activate(priv, true);
+               bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+               mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
+       } else {
+               /* Deactivate 11h functions in the firmware */
+               mwifiex_11h_activate(priv, false);
+               bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
+       }
+}
index ecf2846..a42a506 100644 (file)
@@ -40,6 +40,7 @@ mwifiex-y += sta_rx.o
 mwifiex-y += uap_txrx.o
 mwifiex-y += cfg80211.o
 mwifiex-y += ethtool.o
+mwifiex-y += 11h.o
 mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_MWIFIEX) += mwifiex.o
 
index 00a8281..ef5fa89 100644 (file)
@@ -20,6 +20,9 @@
 #include "cfg80211.h"
 #include "main.h"
 
+static char *reg_alpha2;
+module_param(reg_alpha2, charp, 0);
+
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
                .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
@@ -2475,6 +2478,27 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
 #endif
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT,
+       .n_patterns = MWIFIEX_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+#endif
+
+static bool mwifiex_is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2 || strlen(alpha2) != 2)
+               return false;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
@@ -2527,16 +2551,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_CUSTOM_REGULATORY |
+                       WIPHY_FLAG_STRICT_REGULATORY |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
        wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
-       wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
-       wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+       wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
@@ -2568,10 +2589,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy_free(wiphy);
                return ret;
        }
-       country_code = mwifiex_11d_code_2_region(priv->adapter->region_code);
-       if (country_code)
-               dev_info(adapter->dev,
-                        "ignoring F/W country code %2.2s\n", country_code);
+
+       if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
+               wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2);
+               regulatory_hint(wiphy, reg_alpha2);
+       } else {
+               country_code = mwifiex_11d_code_2_region(adapter->region_code);
+               if (country_code)
+                       wiphy_info(wiphy, "ignoring F/W country code %2.2s\n",
+                                  country_code);
+       }
 
        adapter->wiphy = wiphy;
        return ret;
index d6ada73..1b45aa5 100644 (file)
@@ -245,6 +245,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HT_BW_20    0
 #define HT_BW_40    1
 
+#define DFS_CHAN_MOVE_TIME      10000
+
 #define HostCmd_CMD_GET_HW_SPEC                       0x0003
 #define HostCmd_CMD_802_11_SCAN                       0x0006
 #define HostCmd_CMD_802_11_GET_LOG                    0x000b
@@ -438,6 +440,7 @@ enum P2P_MODES {
 #define EVENT_BW_CHANGE                 0x00000048
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
+#define EVENT_CHANNEL_SWITCH_ANN        0x00000050
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
 #define EVENT_ID_MASK                   0xffff
@@ -975,6 +978,7 @@ enum SNMP_MIB_INDEX {
        LONG_RETRY_LIM_I = 7,
        FRAG_THRESH_I = 8,
        DOT11D_I = 9,
+       DOT11H_I = 10,
 };
 
 #define MAX_SNMP_BUF_SIZE   128
@@ -1206,6 +1210,18 @@ struct host_cmd_ds_sta_deauth {
        __le16 reason;
 } __packed;
 
+struct mwifiex_ie_types_pwr_capability {
+       struct mwifiex_ie_types_header header;
+       s8 min_pwr;
+       s8 max_pwr;
+};
+
+struct mwifiex_ie_types_local_pwr_constraint {
+       struct mwifiex_ie_types_header header;
+       u8 chan;
+       u8 constraint;
+};
+
 struct mwifiex_ie_types_wmm_param_set {
        struct mwifiex_ie_types_header header;
        u8 wmm_ie[1];
index c7f11c0..caaf4bd 100644 (file)
@@ -52,84 +52,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
        return 0;
 }
 
-static void scan_delay_timer_fn(unsigned long data)
-{
-       struct mwifiex_private *priv = (struct mwifiex_private *)data;
-       struct mwifiex_adapter *adapter = priv->adapter;
-       struct cmd_ctrl_node *cmd_node, *tmp_node;
-       unsigned long flags;
-
-       if (adapter->surprise_removed)
-               return;
-
-       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
-               /*
-                * Abort scan operation by cancelling all pending scan
-                * commands
-                */
-               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-               list_for_each_entry_safe(cmd_node, tmp_node,
-                                        &adapter->scan_pending_q, list) {
-                       list_del(&cmd_node->list);
-                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-               }
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
-               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
-               adapter->scan_processing = false;
-               adapter->scan_delay_cnt = 0;
-               adapter->empty_tx_q_cnt = 0;
-               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
-
-               if (priv->scan_request) {
-                       dev_dbg(adapter->dev, "info: aborting scan\n");
-                       cfg80211_scan_done(priv->scan_request, 1);
-                       priv->scan_request = NULL;
-               } else {
-                       priv->scan_aborting = false;
-                       dev_dbg(adapter->dev, "info: scan already aborted\n");
-               }
-               goto done;
-       }
-
-       if (!atomic_read(&priv->adapter->is_tx_received)) {
-               adapter->empty_tx_q_cnt++;
-               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
-                       /*
-                        * No Tx traffic for 200msec. Get scan command from
-                        * scan pending queue and put to cmd pending queue to
-                        * resume scan operation
-                        */
-                       adapter->scan_delay_cnt = 0;
-                       adapter->empty_tx_q_cnt = 0;
-                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-                       cmd_node = list_first_entry(&adapter->scan_pending_q,
-                                                   struct cmd_ctrl_node, list);
-                       list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-
-                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
-                                                       true);
-                       queue_work(adapter->workqueue, &adapter->main_work);
-                       goto done;
-               }
-       } else {
-               adapter->empty_tx_q_cnt = 0;
-       }
-
-       /* Delay scan operation further by 20msec */
-       mod_timer(&priv->scan_delay_timer, jiffies +
-                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
-       adapter->scan_delay_cnt++;
-
-done:
-       if (atomic_read(&priv->adapter->is_tx_received))
-               atomic_set(&priv->adapter->is_tx_received, false);
-
-       return;
-}
-
 /*
  * This function initializes the private structure and sets default
  * values to the members.
@@ -211,8 +133,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
 
        priv->scan_block = false;
 
-       setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn,
-                   (unsigned long)priv);
+       priv->csa_chan = 0;
+       priv->csa_expire_time = 0;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
index 122175a..1c8a771 100644 (file)
@@ -534,6 +534,8 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc);
 
+       mwifiex_11h_process_join(priv, &pos, bss_desc);
+
        cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN);
 
        /* Set the Capability info at last */
index 5bc7ef8..e15ab72 100644 (file)
@@ -28,6 +28,84 @@ const char driver_version[] = "mwifiex " VERSION " (%s) ";
 static char *cal_data_cfg;
 module_param(cal_data_cfg, charp, 0);
 
+static void scan_delay_timer_fn(unsigned long data)
+{
+       struct mwifiex_private *priv = (struct mwifiex_private *)data;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmd_node, *tmp_node;
+       unsigned long flags;
+
+       if (adapter->surprise_removed)
+               return;
+
+       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+               /*
+                * Abort scan operation by cancelling all pending scan
+                * commands
+                */
+               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+               list_for_each_entry_safe(cmd_node, tmp_node,
+                                        &adapter->scan_pending_q, list) {
+                       list_del(&cmd_node->list);
+                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               }
+               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->scan_processing = false;
+               adapter->scan_delay_cnt = 0;
+               adapter->empty_tx_q_cnt = 0;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+               if (priv->scan_request) {
+                       dev_dbg(adapter->dev, "info: aborting scan\n");
+                       cfg80211_scan_done(priv->scan_request, 1);
+                       priv->scan_request = NULL;
+               } else {
+                       priv->scan_aborting = false;
+                       dev_dbg(adapter->dev, "info: scan already aborted\n");
+               }
+               goto done;
+       }
+
+       if (!atomic_read(&priv->adapter->is_tx_received)) {
+               adapter->empty_tx_q_cnt++;
+               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
+                       /*
+                        * No Tx traffic for 200msec. Get scan command from
+                        * scan pending queue and put to cmd pending queue to
+                        * resume scan operation
+                        */
+                       adapter->scan_delay_cnt = 0;
+                       adapter->empty_tx_q_cnt = 0;
+                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+                       cmd_node = list_first_entry(&adapter->scan_pending_q,
+                                                   struct cmd_ctrl_node, list);
+                       list_del(&cmd_node->list);
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+
+                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+                                                       true);
+                       queue_work(adapter->workqueue, &adapter->main_work);
+                       goto done;
+               }
+       } else {
+               adapter->empty_tx_q_cnt = 0;
+       }
+
+       /* Delay scan operation further by 20msec */
+       mod_timer(&priv->scan_delay_timer, jiffies +
+                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+       adapter->scan_delay_cnt++;
+
+done:
+       if (atomic_read(&priv->adapter->is_tx_received))
+               atomic_set(&priv->adapter->is_tx_received, false);
+
+       return;
+}
+
 /*
  * This function registers the device and performs all the necessary
  * initializations.
@@ -75,6 +153,10 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
 
                adapter->priv[i]->adapter = adapter;
                adapter->priv_num++;
+
+               setup_timer(&adapter->priv[i]->scan_delay_timer,
+                           scan_delay_timer_fn,
+                           (unsigned long)adapter->priv[i]);
        }
        mwifiex_init_lock_list(adapter);
 
@@ -587,9 +669,8 @@ static void mwifiex_set_multicast_list(struct net_device *dev)
                mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
        } else {
                mcast_list.mode = MWIFIEX_MULTICAST_MODE;
-               if (netdev_mc_count(dev))
-                       mcast_list.num_multicast_addr =
-                               mwifiex_copy_mcast_addr(&mcast_list, dev);
+               mcast_list.num_multicast_addr =
+                       mwifiex_copy_mcast_addr(&mcast_list, dev);
        }
        mwifiex_request_set_multicast_list(priv, &mcast_list);
 }
index 0832c24..3da73d3 100644 (file)
@@ -309,6 +309,9 @@ struct mwifiex_bssdescriptor {
        u16 wapi_offset;
        u8 *beacon_buf;
        u32 beacon_buf_size;
+       u8 sensed_11h;
+       u8 local_constraint;
+       u8 chan_sw_ie_present;
 };
 
 struct mwifiex_current_bss_params {
@@ -510,6 +513,8 @@ struct mwifiex_private {
        u32 mgmt_frame_mask;
        struct mwifiex_roc_cfg roc_cfg;
        bool scan_aborting;
+       u8 csa_chan;
+       unsigned long csa_expire_time;
 };
 
 enum mwifiex_ba_status {
@@ -1018,6 +1023,24 @@ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
        return (*(u32 *)skb->data == PKT_TYPE_MGMT);
 }
 
+/* This function retrieves channel closed for operation by Channel
+ * Switch Announcement.
+ */
+static inline u8
+mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv)
+{
+       if (!priv->csa_chan)
+               return 0;
+
+       /* Clear csa channel, if DFS channel move time has passed */
+       if (jiffies > priv->csa_expire_time) {
+               priv->csa_chan = 0;
+               priv->csa_expire_time = 0;
+       }
+
+       return priv->csa_chan;
+}
+
 int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
                             u32 func_init_shutdown);
 int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8);
@@ -1119,6 +1142,10 @@ u8 *mwifiex_11d_code_2_region(u8 code);
 void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
                              struct mwifiex_sta_node *node);
 
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+                             struct mwifiex_bssdescriptor *bss_desc);
+int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
+
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
 #ifdef CONFIG_DEBUG_FS
index 801b6b7..c447d9b 100644 (file)
@@ -391,6 +391,12 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv,
                return 0;
        }
 
+       if (bss_desc->chan_sw_ie_present) {
+               dev_err(adapter->dev,
+                       "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
+               return -1;
+       }
+
        if (mwifiex_is_bss_wapi(priv, bss_desc)) {
                dev_dbg(adapter->dev, "info: return success for WAPI AP\n");
                return 0;
@@ -569,6 +575,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                return -1;
        }
 
+       /* Check csa channel expiry before preparing scan list */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
 
        /* Set the temp channel struct pointer to the start of the desired
@@ -598,6 +607,11 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                while (tlv_idx < max_chan_per_scan &&
                       tmp_chan_list->chan_number && !done_early) {
 
+                       if (tmp_chan_list->chan_number == priv->csa_chan) {
+                               tmp_chan_list++;
+                               continue;
+                       }
+
                        dev_dbg(priv->adapter->dev,
                                "info: Scan: Chan(%3d), Radio(%d),"
                                " Mode(%d, %d), Dur(%d)\n",
@@ -1169,6 +1183,19 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
                        bss_entry->erp_flags = *(current_ptr + 2);
                        break;
 
+               case WLAN_EID_PWR_CONSTRAINT:
+                       bss_entry->local_constraint = *(current_ptr + 2);
+                       bss_entry->sensed_11h = true;
+                       break;
+
+               case WLAN_EID_CHANNEL_SWITCH:
+                       bss_entry->chan_sw_ie_present = true;
+               case WLAN_EID_PWR_CAPABILITY:
+               case WLAN_EID_TPC_REPORT:
+               case WLAN_EID_QUIET:
+                       bss_entry->sensed_11h = true;
+                   break;
+
                case WLAN_EID_EXT_SUPP_RATES:
                        /*
                         * Only process extended supported rate
@@ -1575,6 +1602,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                goto check_next_scan;
        }
 
+       /* Check csa channel expiry before parsing scan response */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
        dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n",
                bytes_left);
@@ -1727,6 +1757,13 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                        struct ieee80211_channel *chan;
                        u8 band;
 
+                       /* Skip entry if on csa closed channel */
+                       if (channel == priv->csa_chan) {
+                               dev_dbg(adapter->dev,
+                                       "Dropping entry on csa closed channel\n");
+                               continue;
+                       }
+
                        band = BAND_G;
                        if (chan_band_tlv) {
                                chan_band =
index 41aafc7..ea265ec 100644 (file)
@@ -427,6 +427,17 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
                break;
 
+       case EVENT_CHANNEL_SWITCH_ANN:
+               dev_dbg(adapter->dev, "event: Channel Switch Announcement\n");
+               priv->csa_expire_time =
+                               jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
+               priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel;
+               ret = mwifiex_send_cmd_async(priv,
+                       HostCmd_CMD_802_11_DEAUTHENTICATE,
+                       HostCmd_ACT_GEN_SET, 0,
+                       priv->curr_bss_params.bss_descriptor.mac_address);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index 1a8a19d..206c3e0 100644 (file)
@@ -104,16 +104,14 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
                } else {
                        priv->curr_pkt_filter &=
                                ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
-                       if (mcast_list->num_multicast_addr) {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: Set multicast list=%d\n",
-                                      mcast_list->num_multicast_addr);
-                               /* Send multicast addresses to firmware */
-                               ret = mwifiex_send_cmd_async(priv,
-                                       HostCmd_CMD_MAC_MULTICAST_ADR,
-                                       HostCmd_ACT_GEN_SET, 0,
-                                       mcast_list);
-                       }
+                       dev_dbg(priv->adapter->dev,
+                               "info: Set multicast list=%d\n",
+                               mcast_list->num_multicast_addr);
+                       /* Send multicast addresses to firmware */
+                       ret = mwifiex_send_cmd_async(priv,
+                               HostCmd_CMD_MAC_MULTICAST_ADR,
+                               HostCmd_ACT_GEN_SET, 0,
+                               mcast_list);
                }
        }
        dev_dbg(priv->adapter->dev,
@@ -180,6 +178,9 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
         */
        bss_desc->disable_11ac = true;
 
+       if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
+               bss_desc->sensed_11h = true;
+
        return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 }
 
@@ -257,30 +258,37 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
        }
 
        if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+               u8 config_bands;
+
                /* Infra mode */
                ret = mwifiex_deauthenticate(priv, NULL);
                if (ret)
                        goto done;
 
-               if (bss_desc) {
-                       u8 config_bands = 0;
+               if (!bss_desc)
+                       return -1;
 
-                       if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band)
-                           == HostCmd_SCAN_RADIO_TYPE_BG)
-                               config_bands = BAND_B | BAND_G | BAND_GN |
-                                              BAND_GAC;
-                       else
-                               config_bands = BAND_A | BAND_AN | BAND_AAC;
+               if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
+                                               HostCmd_SCAN_RADIO_TYPE_BG)
+                       config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC;
+               else
+                       config_bands = BAND_A | BAND_AN | BAND_AAC;
 
-                       if (!((config_bands | adapter->fw_bands) &
-                             ~adapter->fw_bands))
-                               adapter->config_bands = config_bands;
-               }
+               if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
+                       adapter->config_bands = config_bands;
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
                if (ret)
                        goto done;
 
+               if (mwifiex_11h_get_csa_closed_channel(priv) ==
+                                                       (u8)bss_desc->channel) {
+                       dev_err(adapter->dev,
+                               "Attempt to reconnect on csa closed chan(%d)\n",
+                               bss_desc->channel);
+                       goto done;
+               }
+
                dev_dbg(adapter->dev, "info: SSID found in scan list ... "
                                      "associating...\n");
 
index 4be3d33..944e884 100644 (file)
@@ -37,6 +37,9 @@
 /* Offset for TOS field in the IP header */
 #define IPTOS_OFFSET 5
 
+static bool enable_tx_amsdu;
+module_param(enable_tx_amsdu, bool, 0644);
+
 /* WMM information IE */
 static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
        0x00, 0x50, 0xf2, 0x02,
@@ -1233,7 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                                mwifiex_send_delba(priv, tid_del, ra, 1);
                        }
                }
-               if (mwifiex_is_amsdu_allowed(priv, tid) &&
+               if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) &&
                    mwifiex_is_11n_aggragation_possible(priv, ptr,
                                                        adapter->tx_buf_size))
                        mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
index 1f9cb55..bdfe637 100644 (file)
@@ -881,7 +881,8 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
 
        if (!upriv->udev) {
                dbg("Device disconnected");
-               return -ENODEV;
+               retval = -ENODEV;
+               goto exit;
        }
 
        if (upriv->read_urb->status != -EINPROGRESS)
index f714373..3d53a09 100644 (file)
@@ -1767,33 +1767,45 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
        .config                 = rt2400pci_config,
 };
 
-static const struct data_queue_desc rt2400pci_queue_rx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2400pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_tx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2400pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1801,11 +1813,7 @@ static const struct rt2x00_ops rt2400pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2400pci_queue_rx,
-       .tx                     = &rt2400pci_queue_tx,
-       .bcn                    = &rt2400pci_queue_bcn,
-       .atim                   = &rt2400pci_queue_atim,
+       .queue_init             = rt2400pci_queue_init,
        .lib                    = &rt2400pci_rt2x00_ops,
        .hw                     = &rt2400pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 77e45b2..0ac5c58 100644 (file)
@@ -2056,33 +2056,45 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
        .config                 = rt2500pci_config,
 };
 
-static const struct data_queue_desc rt2500pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2500pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2090,11 +2102,7 @@ static const struct rt2x00_ops rt2500pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2500pci_queue_rx,
-       .tx                     = &rt2500pci_queue_tx,
-       .bcn                    = &rt2500pci_queue_bcn,
-       .atim                   = &rt2500pci_queue_atim,
+       .queue_init             = rt2500pci_queue_init,
        .lib                    = &rt2500pci_rt2x00_ops,
        .hw                     = &rt2500pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index a7f7b36..85acc79 100644 (file)
@@ -1867,33 +1867,45 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
        .config                 = rt2500usb_config,
 };
 
-static const struct data_queue_desc rt2500usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2500usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb_bcn),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1901,11 +1913,7 @@ static const struct rt2x00_ops rt2500usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt2500usb_queue_rx,
-       .tx                     = &rt2500usb_queue_tx,
-       .bcn                    = &rt2500usb_queue_bcn,
-       .atim                   = &rt2500usb_queue_atim,
+       .queue_init             = rt2500usb_queue_init,
        .lib                    = &rt2500usb_rt2x00_ops,
        .hw                     = &rt2500usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 330f1d2..7c74782 100644 (file)
@@ -1186,29 +1186,43 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800pci_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2800pci_queue_init(struct data_queue *queue)
+{
+       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;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_tx = {
-       .entry_num              = 64,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 64;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2800pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1217,10 +1231,7 @@ static const struct rt2x00_ops rt2800pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXWI_DESC_SIZE,
-       .rx                     = &rt2800pci_queue_rx,
-       .tx                     = &rt2800pci_queue_tx,
-       .bcn                    = &rt2800pci_queue_bcn,
+       .queue_init             = rt2800pci_queue_init,
        .lib                    = &rt2800pci_rt2x00_ops,
        .drv                    = &rt2800pci_rt2800_ops,
        .hw                     = &rt2800pci_mac80211_ops,
index c71a48d..7edd903 100644 (file)
@@ -849,85 +849,63 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800usb_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_tx = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2800usb_queue_init(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
 
-static const struct rt2x00_ops rt2800usb_ops = {
-       .name                   = KBUILD_MODNAME,
-       .drv_data_size          = sizeof(struct rt2800_drv_data),
-       .max_ap_intf            = 8,
-       .eeprom_size            = EEPROM_SIZE,
-       .rf_size                = RF_SIZE,
-       .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE,
-       .rx                     = &rt2800usb_queue_rx,
-       .tx                     = &rt2800usb_queue_tx,
-       .bcn                    = &rt2800usb_queue_bcn,
-       .lib                    = &rt2800usb_rt2x00_ops,
-       .drv                    = &rt2800usb_rt2800_ops,
-       .hw                     = &rt2800usb_mac80211_ops,
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-       .debugfs                = &rt2800_rt2x00debug,
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-};
+       if (rt2x00_rt(rt2x00dev, RT5592)) {
+               txwi_size = TXWI_DESC_SIZE_5592;
+               rxwi_size = RXWI_DESC_SIZE_5592;
+       } else {
+               txwi_size = TXWI_DESC_SIZE;
+               rxwi_size = RXWI_DESC_SIZE;
+       }
 
-static const struct data_queue_desc rt2800usb_queue_rx_5592 = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXINFO_DESC_SIZE;
+               queue->winfo_size = rxwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_tx_5592 = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 16;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_bcn_5592 = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
-static const struct rt2x00_ops rt2800usb_ops_5592 = {
+static const struct rt2x00_ops rt2800usb_ops = {
        .name                   = KBUILD_MODNAME,
        .drv_data_size          = sizeof(struct rt2800_drv_data),
        .max_ap_intf            = 8,
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
-       .rx                     = &rt2800usb_queue_rx_5592,
-       .tx                     = &rt2800usb_queue_tx_5592,
-       .bcn                    = &rt2800usb_queue_bcn_5592,
+       .queue_init             = rt2800usb_queue_init,
        .lib                    = &rt2800usb_rt2x00_ops,
        .drv                    = &rt2800usb_rt2800_ops,
        .hw                     = &rt2800usb_mac80211_ops,
@@ -1248,15 +1226,15 @@ static struct usb_device_id rt2800usb_device_table[] = {
 #endif
 #ifdef CONFIG_RT2800USB_RT55XX
        /* Arcadyan */
-       { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a32) },
        /* AVM GmbH */
-       { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 },
+       { USB_DEVICE(0x057c, 0x8501) },
        /* D-Link DWA-160-B2 */
-       { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 },
+       { USB_DEVICE(0x2001, 0x3c1a) },
        /* Proware */
-       { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a13) },
        /* Ralink */
-       { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 },
+       { USB_DEVICE(0x148f, 0x5572) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1361,9 +1339,6 @@ MODULE_LICENSE("GPL");
 static int rt2800usb_probe(struct usb_interface *usb_intf,
                           const struct usb_device_id *id)
 {
-       if (id->driver_info == 5592)
-               return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592);
-
        return rt2x00usb_probe(usb_intf, &rt2800usb_ops);
 }
 
index 7510723..ee3fc57 100644 (file)
@@ -648,11 +648,7 @@ struct rt2x00_ops {
        const unsigned int eeprom_size;
        const unsigned int rf_size;
        const unsigned int tx_queues;
-       const unsigned int extra_tx_headroom;
-       const struct data_queue_desc *rx;
-       const struct data_queue_desc *tx;
-       const struct data_queue_desc *bcn;
-       const struct data_queue_desc *atim;
+       void (*queue_init)(struct data_queue *queue);
        const struct rt2x00lib_ops *lib;
        const void *drv;
        const struct ieee80211_ops *hw;
@@ -1010,6 +1006,9 @@ struct rt2x00_dev {
         */
        struct list_head bar_list;
        spinlock_t bar_list_lock;
+
+       /* Extra TX headroom required for alignment purposes. */
+       unsigned int extra_tx_headroom;
 };
 
 struct rt2x00_bar_list_entry {
index 6a20172..f03e3bb 100644 (file)
@@ -334,7 +334,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
        /*
         * Remove the extra tx headroom from the skb.
         */
-       skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom);
+       skb_pull(entry->skb, rt2x00dev->extra_tx_headroom);
 
        /*
         * Signal that the TX descriptor is no longer in the skb.
@@ -1049,7 +1049,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->extra_tx_headroom =
                max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM,
-                     rt2x00dev->ops->extra_tx_headroom);
+                     rt2x00dev->extra_tx_headroom);
 
        /*
         * Take TX headroom required for alignment into account.
@@ -1256,6 +1256,17 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->wiphy->n_iface_combinations = 1;
 }
 
+static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev)
+{
+       if (WARN_ON(!rt2x00dev->tx))
+               return 0;
+
+       if (rt2x00_is_usb(rt2x00dev))
+               return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size;
+
+       return rt2x00dev->tx[0].winfo_size;
+}
+
 /*
  * driver allocation handlers.
  */
@@ -1330,13 +1341,16 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
        if (retval)
                goto exit;
 
+       /* Cache TX headroom value */
+       rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev);
+
        /*
         * Determine which operating modes are supported, all modes
         * which require beaconing, depend on the availability of
         * beacon entries.
         */
        rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-       if (rt2x00dev->ops->bcn->entry_num > 0)
+       if (rt2x00dev->bcn->limit > 0)
                rt2x00dev->hw->wiphy->interface_modes |=
                    BIT(NL80211_IFTYPE_ADHOC) |
                    BIT(NL80211_IFTYPE_AP) |
index 5efbbbd..6c0a91f 100644 (file)
@@ -542,8 +542,8 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry,
        /*
         * Add the requested extra tx headroom in front of the skb.
         */
-       skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom);
-       memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom);
+       skb_push(entry->skb, rt2x00dev->extra_tx_headroom);
+       memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom);
 
        /*
         * Call the driver's write_tx_data function, if it exists.
@@ -596,7 +596,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct ieee80211_bar *bar = (void *) (entry->skb->data +
-                                   rt2x00dev->ops->extra_tx_headroom);
+                                   rt2x00dev->extra_tx_headroom);
        struct rt2x00_bar_list_entry *bar_entry;
 
        if (likely(!ieee80211_is_back_req(bar->frame_control)))
@@ -1161,8 +1161,7 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static int rt2x00queue_alloc_entries(struct data_queue *queue,
-                                    const struct data_queue_desc *qdesc)
+static int rt2x00queue_alloc_entries(struct data_queue *queue)
 {
        struct queue_entry *entries;
        unsigned int entry_size;
@@ -1173,7 +1172,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
        /*
         * Allocate all queue entries.
         */
-       entry_size = sizeof(*entries) + qdesc->priv_size;
+       entry_size = sizeof(*entries) + queue->priv_size;
        entries = kcalloc(queue->limit, entry_size, GFP_KERNEL);
        if (!entries)
                return -ENOMEM;
@@ -1189,7 +1188,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
                entries[i].entry_idx = i;
                entries[i].priv_data =
                    QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit,
-                                           sizeof(*entries), qdesc->priv_size);
+                                           sizeof(*entries), queue->priv_size);
        }
 
 #undef QUEUE_ENTRY_PRIV_OFFSET
@@ -1231,23 +1230,22 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev)
        struct data_queue *queue;
        int status;
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx);
+       status = rt2x00queue_alloc_entries(rt2x00dev->rx);
        if (status)
                goto exit;
 
        tx_queue_for_each(rt2x00dev, queue) {
-               status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx);
+               status = rt2x00queue_alloc_entries(queue);
                if (status)
                        goto exit;
        }
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn);
+       status = rt2x00queue_alloc_entries(rt2x00dev->bcn);
        if (status)
                goto exit;
 
        if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) {
-               status = rt2x00queue_alloc_entries(rt2x00dev->atim,
-                                                  rt2x00dev->ops->atim);
+               status = rt2x00queue_alloc_entries(rt2x00dev->atim);
                if (status)
                        goto exit;
        }
@@ -1278,38 +1276,9 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static const struct data_queue_desc *
-rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev,
-                            enum data_queue_qid qid)
-{
-       switch (qid) {
-       case QID_RX:
-               return rt2x00dev->ops->rx;
-
-       case QID_AC_BE:
-       case QID_AC_BK:
-       case QID_AC_VO:
-       case QID_AC_VI:
-               return rt2x00dev->ops->tx;
-
-       case QID_BEACON:
-               return rt2x00dev->ops->bcn;
-
-       case QID_ATIM:
-               return rt2x00dev->ops->atim;
-
-       default:
-               break;
-       }
-
-       return NULL;
-}
-
 static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
                             struct data_queue *queue, enum data_queue_qid qid)
 {
-       const struct data_queue_desc *qdesc;
-
        mutex_init(&queue->status_lock);
        spin_lock_init(&queue->tx_lock);
        spin_lock_init(&queue->index_lock);
@@ -1321,14 +1290,9 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
        queue->cw_min = 5;
        queue->cw_max = 10;
 
-       qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid);
-       BUG_ON(!qdesc);
+       rt2x00dev->ops->queue_init(queue);
 
-       queue->limit = qdesc->entry_num;
-       queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10);
-       queue->data_size = qdesc->data_size;
-       queue->desc_size = qdesc->desc_size;
-       queue->winfo_size = qdesc->winfo_size;
+       queue->threshold = DIV_ROUND_UP(queue->limit, 10);
 }
 
 int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev)
index 4a7b34e..ebe1172 100644 (file)
@@ -453,6 +453,7 @@ enum data_queue_flags {
  * @cw_max: The cw max value for outgoing frames (field ignored in RX queue).
  * @data_size: Maximum data size for the frames in this queue.
  * @desc_size: Hardware descriptor size for the data in this queue.
+ * @priv_size: Size of per-queue_entry private data.
  * @usb_endpoint: Device endpoint used for communication (USB only)
  * @usb_maxpacket: Max packet size for given endpoint (USB only)
  */
@@ -481,30 +482,12 @@ struct data_queue {
        unsigned short data_size;
        unsigned char  desc_size;
        unsigned char  winfo_size;
+       unsigned short priv_size;
 
        unsigned short usb_endpoint;
        unsigned short usb_maxpacket;
 };
 
-/**
- * struct data_queue_desc: Data queue description
- *
- * The information in this structure is used by drivers
- * to inform rt2x00lib about the creation of the data queue.
- *
- * @entry_num: Maximum number of entries for a queue.
- * @data_size: Maximum data size for the frames in this queue.
- * @desc_size: Hardware descriptor size for the data in this queue.
- * @priv_size: Size of per-queue_entry private data.
- */
-struct data_queue_desc {
-       unsigned short entry_num;
-       unsigned short data_size;
-       unsigned char  desc_size;
-       unsigned char  winfo_size;
-       unsigned short priv_size;
-};
-
 /**
  * queue_end - Return pointer to the last queue (HELPER MACRO).
  * @__dev: Pointer to &struct rt2x00_dev
index 7e1759b..53754bc 100644 (file)
@@ -3025,26 +3025,40 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
        .config                 = rt61pci_config,
 };
 
-static const struct data_queue_desc rt61pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt61pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt61pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -3052,10 +3066,7 @@ static const struct rt2x00_ops rt61pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt61pci_queue_rx,
-       .tx                     = &rt61pci_queue_tx,
-       .bcn                    = &rt61pci_queue_bcn,
+       .queue_init             = rt61pci_queue_init,
        .lib                    = &rt61pci_rt2x00_ops,
        .hw                     = &rt61pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 377e09b..1616ed4 100644 (file)
@@ -2359,26 +2359,40 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
        .config                 = rt73usb_config,
 };
 
-static const struct data_queue_desc rt73usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt73usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt73usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2386,10 +2400,7 @@ static const struct rt2x00_ops rt73usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt73usb_queue_rx,
-       .tx                     = &rt73usb_queue_tx,
-       .bcn                    = &rt73usb_queue_bcn,
+       .queue_init             = rt73usb_queue_init,
        .lib                    = &rt73usb_rt2x00_ops,
        .hw                     = &rt73usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 953f1a0..2119313 100644 (file)
@@ -104,7 +104,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
                        tx_agc[RF90_PATH_A] = 0x10101010;
                        tx_agc[RF90_PATH_B] = 0x10101010;
                } else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
-                          TXHIGHPWRLEVEL_LEVEL1) {
+                          TXHIGHPWRLEVEL_LEVEL2) {
                        tx_agc[RF90_PATH_A] = 0x00000000;
                        tx_agc[RF90_PATH_B] = 0x00000000;
                } else{
index 826f085..2bd5985 100644 (file)
@@ -359,6 +359,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = {
        {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
        {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/
        {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/
+       {RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/
        {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/
        {}
 };
index 4c67c2f..c7dc6fe 100644 (file)
@@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl)
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
-       /*
-        * Set WSPI_INIT_COMMAND
+       /* Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
        cmd[2] = 0xff;
@@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi)
        wl->if_ops = &wl1251_spi_ops;
 
        /* This is the only SPI value that we need to set here, the rest
-        * comes from the board-peripherals file */
+        * comes from the board-peripherals file
+        */
        spi->bits_per_word = 32;
 
        ret = spi_setup(spi);
@@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = {
        .remove         = wl1251_spi_remove,
 };
 
-static int __init wl1251_spi_init(void)
-{
-       int ret;
-
-       ret = spi_register_driver(&wl1251_spi_driver);
-       if (ret < 0) {
-               wl1251_error("failed to register spi driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
-}
-
-static void __exit wl1251_spi_exit(void)
-{
-       spi_unregister_driver(&wl1251_spi_driver);
-
-       wl1251_notice("unloaded");
-}
-
-module_init(wl1251_spi_init);
-module_exit(wl1251_spi_exit);
+module_spi_driver(wl1251_spi_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
index 9fa692d..7aa0eb8 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/ip.h>
 #include <linux/firmware.h>
+#include <linux/etherdevice.h>
 
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
@@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
                .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
        },
        [PART_PHY_INIT] = {
-               .mem  = { .start = 0x80926000,
-                         .size = sizeof(struct wl18xx_mac_and_phy_params) },
+               .mem  = { .start = WL18XX_PHY_INIT_MEM_ADDR,
+                         .size  = WL18XX_PHY_INIT_MEM_SIZE },
                .reg  = { .start = 0x00000000, .size = 0x00000000 },
                .mem2 = { .start = 0x00000000, .size = 0x00000000 },
                .mem3 = { .start = 0x00000000, .size = 0x00000000 },
@@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        u32 tmp;
        int ret;
 
+       BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
+               WL18XX_PHY_INIT_MEM_SIZE);
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
        if (ret < 0)
                goto out;
@@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
 
        ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
+       if (ret < 0)
+               goto out;
+
+       /*
+        * Workaround for FDSP code RAM corruption (needed for PG2.1
+        * and newer; for older chips it's a NOP).  Change FDSP clock
+        * settings so that it's muxed to the ATGP clock instead of
+        * its own clock.
+        */
+
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+       if (ret < 0)
+               goto out;
+
+       /* disable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_DISABLE);
+       if (ret < 0)
+               goto out;
+
+       /* set ATPG clock toward FDSP Code RAM rather than its own clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CODERAM_FUNC_CLK_SEL);
+       if (ret < 0)
+               goto out;
+
+       /* re-enable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_ENABLE);
 
 out:
        return ret;
@@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl)
                ((mac1 & 0xff000000) >> 24);
        wl->fuse_nic_addr = (mac1 & 0xffffff);
 
+       if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) {
+               u8 mac[ETH_ALEN];
+
+               eth_random_addr(mac);
+
+               wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2];
+               wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5];
+               wl1271_warning("MAC address from fuse not available, using random locally administered addresses.");
+       }
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
 
 out:
index 6306e04..05dd8ba 100644 (file)
@@ -38,6 +38,9 @@
 #define WL18XX_REG_BOOT_PART_SIZE  0x00014578
 
 #define WL18XX_PHY_INIT_MEM_ADDR   0x80926000
+#define WL18XX_PHY_END_MEM_ADDR           0x8093CA44
+#define WL18XX_PHY_INIT_MEM_SIZE \
+       (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR)
 
 #define WL18XX_SDIO_WSPI_BASE          (WL18XX_REGISTERS_BASE)
 #define WL18XX_REG_CONFIG_BASE         (WL18XX_REGISTERS_BASE + 0x02000)
@@ -217,4 +220,16 @@ static const char * const rdl_names[] = {
        [RDL_4_SP]      = "1897 MIMO",
 };
 
+/* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */
+#define WL18XX_PHY_FPGA_SPARE_1                0x8093CA40
+
+/* command to disable FDSP clock */
+#define MEM_FDSP_CLK_120_DISABLE        0x80000000
+
+/* command to set ATPG clock toward FDSP Code RAM rather than its own clock */
+#define MEM_FDSP_CODERAM_FUNC_CLK_SEL  0xC0000000
+
+/* command to re-enable FDSP clock */
+#define MEM_FDSP_CLK_120_ENABLE                0x40000000
+
 #endif /* __REG_H__ */
index b21398f..4f23931 100644 (file)
@@ -1,5 +1,5 @@
 wlcore-objs            = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
-                         boot.o init.o debugfs.o scan.o
+                         boot.o init.o debugfs.o scan.o sysfs.o
 
 wlcore_spi-objs        = spi.o
 wlcore_sdio-objs       = sdio.o
index 953111a..b8db55c 100644 (file)
@@ -1,10 +1,9 @@
 
 /*
- * This file is part of wl1271
+ * This file is part of wlcore
  *
  * Copyright (C) 2008-2010 Nokia Corporation
- *
- * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 
 #include <linux/module.h>
 #include <linux/firmware.h>
-#include <linux/delay.h>
-#include <linux/spi/spi.h>
-#include <linux/crc32.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/wl12xx.h>
-#include <linux/sched.h>
 #include <linux/interrupt.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "wl12xx_80211.h"
 #include "io.h"
-#include "event.h"
 #include "tx.h"
-#include "rx.h"
 #include "ps.h"
 #include "init.h"
 #include "debugfs.h"
-#include "cmd.h"
-#include "boot.h"
 #include "testmode.h"
 #include "scan.h"
 #include "hw_ops.h"
-
-#define WL1271_BOOT_RETRIES 3
+#include "sysfs.h"
 
 #define WL1271_BOOT_RETRIES 3
 
@@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 static void wlcore_op_stop_locked(struct wl1271 *wl);
 static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 
-static int wl12xx_set_authorized(struct wl1271 *wl,
-                                struct wl12xx_vif *wlvif)
+static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
@@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                                  sizeof(*wl->fw_status_2) +
                                  wl->fw_status_priv_len, GFP_KERNEL);
        if (!wl->fw_status_1)
@@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl)
                                (((u8 *) wl->fw_status_1) +
                                WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
 
-       wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
+       wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
        if (!wl->tx_res_if) {
                kfree(wl->fw_status_1);
                return -ENOMEM;
@@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl,
        return 0;
 }
 
-static void wl1271_configure_resume(struct wl1271 *wl,
-                                   struct wl12xx_vif *wlvif)
+static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret = 0;
        bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
@@ -2603,6 +2589,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
+       cancel_delayed_work_sync(&wlvif->channel_switch_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                if (ret < 0)
                        return ret;
 
-               /* the default WEP key needs to be configured at least once */
-               if (key_type == KEY_WEP) {
-                       ret = wl12xx_cmd_set_default_wep_key(wl,
-                                                       wlvif->default_key,
-                                                       wlvif->sta.hlid);
-                       if (ret < 0)
-                               return ret;
-               }
        }
 
        return 0;
@@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif,
+                                         int key_idx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
+                    key_idx);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EAGAIN;
+               goto out_unlock;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       wlvif->default_key = key_idx;
+
+       /* the default WEP key needs to be configured at least once */
+       if (wlvif->encryption_type == KEY_WEP) {
+               ret = wl12xx_cmd_set_default_wep_key(wl,
+                               key_idx,
+                               wlvif->sta.hlid);
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+
+out_unlock:
+       mutex_unlock(&wl->mutex);
+}
+
 void wlcore_regdomain_config(struct wl1271 *wl)
 {
        int ret;
@@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
        struct ieee80211_hdr *hdr;
        u32 min_rate;
        int ret;
-       int ieoffset = offsetof(struct ieee80211_mgmt,
-                               u.beacon.variable);
+       int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
        struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
        u16 tmpl_id;
 
@@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists &&
-           (changed & BSS_CHANGED_HT)) {
+       if (sta_exists) {
                bool enabled =
                        bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
 
@@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .ampdu_action = wl1271_op_ampdu_action,
        .tx_frames_pending = wl1271_tx_frames_pending,
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
+       .set_default_unicast_key = wl1271_op_set_default_key_idx,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
        .remain_on_channel = wlcore_op_remain_on_channel,
@@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
        return idx;
 }
 
-static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
-                                              struct device_attribute *attr,
-                                              char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
-                      wl->sg_enabled);
-       mutex_unlock(&wl->mutex);
-
-       return len;
-
-}
-
-static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
-                                               struct device_attribute *attr,
-                                               const char *buf, size_t count)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       unsigned long res;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &res);
-       if (ret < 0) {
-               wl1271_warning("incorrect value written to bt_coex_mode");
-               return count;
-       }
-
-       mutex_lock(&wl->mutex);
-
-       res = !!res;
-
-       if (res == wl->sg_enabled)
-               goto out;
-
-       wl->sg_enabled = res;
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       ret = wl1271_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       wl1271_acx_sg_enable(wl, wl->sg_enabled);
-       wl1271_ps_elp_sleep(wl);
-
- out:
-       mutex_unlock(&wl->mutex);
-       return count;
-}
-
-static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
-                  wl1271_sysfs_show_bt_coex_state,
-                  wl1271_sysfs_store_bt_coex_state);
-
-static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
-                                          struct device_attribute *attr,
-                                          char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       if (wl->hw_pg_ver >= 0)
-               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
-       else
-               len = snprintf(buf, len, "n/a\n");
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
-                  wl1271_sysfs_show_hw_pg_ver, NULL);
-
-static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
-                                      struct bin_attribute *bin_attr,
-                                      char *buffer, loff_t pos, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-       int ret;
-
-       ret = mutex_lock_interruptible(&wl->mutex);
-       if (ret < 0)
-               return -ERESTARTSYS;
-
-       /* Let only one thread read the log at a time, blocking others */
-       while (wl->fwlog_size == 0) {
-               DEFINE_WAIT(wait);
-
-               prepare_to_wait_exclusive(&wl->fwlog_waitq,
-                                         &wait,
-                                         TASK_INTERRUPTIBLE);
-
-               if (wl->fwlog_size != 0) {
-                       finish_wait(&wl->fwlog_waitq, &wait);
-                       break;
-               }
-
-               mutex_unlock(&wl->mutex);
-
-               schedule();
-               finish_wait(&wl->fwlog_waitq, &wait);
-
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-
-               ret = mutex_lock_interruptible(&wl->mutex);
-               if (ret < 0)
-                       return -ERESTARTSYS;
-       }
-
-       /* Check if the fwlog is still valid */
-       if (wl->fwlog_size < 0) {
-               mutex_unlock(&wl->mutex);
-               return 0;
-       }
-
-       /* Seeking is not supported - old logs are not kept. Disregard pos. */
-       len = min(count, (size_t)wl->fwlog_size);
-       wl->fwlog_size -= len;
-       memcpy(buffer, wl->fwlog, len);
-
-       /* Make room for new messages */
-       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
-
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static struct bin_attribute fwlog_attr = {
-       .attr = {.name = "fwlog", .mode = S_IRUSR},
-       .read = wl1271_sysfs_read_fwlog,
-};
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        return 0;
 }
 
-#define WL1271_DEFAULT_CHANNEL 0
-
 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                                     u32 mbox_size)
 {
@@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                goto err_hw;
        }
 
-       wl->channel = WL1271_DEFAULT_CHANNEL;
+       wl->channel = 0;
        wl->rx_counter = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->band = IEEE80211_BAND_2GHZ;
@@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        wake_up_interruptible_all(&wl->fwlog_waitq);
        mutex_unlock(&wl->mutex);
 
-       device_remove_bin_file(wl->dev, &fwlog_attr);
-
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+       wlcore_sysfs_free(wl);
 
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
        kfree(wl->buffer_32);
        kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
@@ -6018,6 +5886,15 @@ int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wlcore_wowlan_support = {
+       .flags = WIPHY_WOWLAN_ANY,
+       .n_patterns = WL1271_MAX_RX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = WL1271_RX_FILTER_MAX_PATTERN_SIZE,
+};
+#endif
+
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
@@ -6071,14 +5948,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (!ret) {
                wl->irq_wake_enabled = true;
                device_init_wakeup(wl->dev, 1);
-               if (pdata->pwr_in_suspend) {
-                       wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
-                       wl->hw->wiphy->wowlan.n_patterns =
-                               WL1271_MAX_RX_FILTERS;
-                       wl->hw->wiphy->wowlan.pattern_min_len = 1;
-                       wl->hw->wiphy->wowlan.pattern_max_len =
-                               WL1271_RX_FILTER_MAX_PATTERN_SIZE;
-               }
+               if (pdata->pwr_in_suspend)
+                       wl->hw->wiphy->wowlan = &wlcore_wowlan_support;
        }
 #endif
        disable_irq(wl->irq);
@@ -6101,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (ret)
                goto out_irq;
 
-       /* Create sysfs file to control bt coex state */
-       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file bt_coex_state");
+       ret = wlcore_sysfs_init(wl);
+       if (ret)
                goto out_unreg;
-       }
-
-       /* Create sysfs file to get HW PG version */
-       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file hw_pg_ver");
-               goto out_bt_coex_state;
-       }
-
-       /* Create sysfs file for the FW log */
-       ret = device_create_bin_file(wl->dev, &fwlog_attr);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file fwlog");
-               goto out_hw_pg_ver;
-       }
 
        wl->initialized = true;
        goto out;
 
-out_hw_pg_ver:
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
-
-out_bt_coex_state:
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
-
 out_unreg:
        wl1271_unregister_hw(wl);
 
index 9654577..98066d4 100644 (file)
@@ -110,7 +110,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
        DECLARE_COMPLETION_ONSTACK(compl);
        unsigned long flags;
        int ret;
-       u32 start_time = jiffies;
+       unsigned long start_time = jiffies;
        bool pending = false;
 
        /*
index e264478..1b0cd98 100644 (file)
@@ -434,19 +434,7 @@ static struct spi_driver wl1271_spi_driver = {
        .remove         = wl1271_remove,
 };
 
-static int __init wl1271_init(void)
-{
-       return spi_register_driver(&wl1271_spi_driver);
-}
-
-static void __exit wl1271_exit(void)
-{
-       spi_unregister_driver(&wl1271_spi_driver);
-}
-
-module_init(wl1271_init);
-module_exit(wl1271_exit);
-
+module_spi_driver(wl1271_spi_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
new file mode 100644 (file)
index 0000000..8e58349
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "wlcore.h"
+#include "debug.h"
+#include "ps.h"
+#include "sysfs.h"
+
+static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
+                      wl->sg_enabled);
+       mutex_unlock(&wl->mutex);
+
+       return len;
+
+}
+
+static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       unsigned long res;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &res);
+       if (ret < 0) {
+               wl1271_warning("incorrect value written to bt_coex_mode");
+               return count;
+       }
+
+       mutex_lock(&wl->mutex);
+
+       res = !!res;
+
+       if (res == wl->sg_enabled)
+               goto out;
+
+       wl->sg_enabled = res;
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl1271_acx_sg_enable(wl, wl->sg_enabled);
+       wl1271_ps_elp_sleep(wl);
+
+ out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
+                  wl1271_sysfs_show_bt_coex_state,
+                  wl1271_sysfs_store_bt_coex_state);
+
+static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       if (wl->hw_pg_ver >= 0)
+               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
+       else
+               len = snprintf(buf, len, "n/a\n");
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
+                  wl1271_sysfs_show_hw_pg_ver, NULL);
+
+static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
+                                      struct bin_attribute *bin_attr,
+                                      char *buffer, loff_t pos, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+       int ret;
+
+       ret = mutex_lock_interruptible(&wl->mutex);
+       if (ret < 0)
+               return -ERESTARTSYS;
+
+       /* Let only one thread read the log at a time, blocking others */
+       while (wl->fwlog_size == 0) {
+               DEFINE_WAIT(wait);
+
+               prepare_to_wait_exclusive(&wl->fwlog_waitq,
+                                         &wait,
+                                         TASK_INTERRUPTIBLE);
+
+               if (wl->fwlog_size != 0) {
+                       finish_wait(&wl->fwlog_waitq, &wait);
+                       break;
+               }
+
+               mutex_unlock(&wl->mutex);
+
+               schedule();
+               finish_wait(&wl->fwlog_waitq, &wait);
+
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+
+               ret = mutex_lock_interruptible(&wl->mutex);
+               if (ret < 0)
+                       return -ERESTARTSYS;
+       }
+
+       /* Check if the fwlog is still valid */
+       if (wl->fwlog_size < 0) {
+               mutex_unlock(&wl->mutex);
+               return 0;
+       }
+
+       /* Seeking is not supported - old logs are not kept. Disregard pos. */
+       len = min(count, (size_t)wl->fwlog_size);
+       wl->fwlog_size -= len;
+       memcpy(buffer, wl->fwlog, len);
+
+       /* Make room for new messages */
+       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
+
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static struct bin_attribute fwlog_attr = {
+       .attr = {.name = "fwlog", .mode = S_IRUSR},
+       .read = wl1271_sysfs_read_fwlog,
+};
+
+int wlcore_sysfs_init(struct wl1271 *wl)
+{
+       int ret;
+
+       /* Create sysfs file to control bt coex state */
+       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file bt_coex_state");
+               goto out;
+       }
+
+       /* Create sysfs file to get HW PG version */
+       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file hw_pg_ver");
+               goto out_bt_coex_state;
+       }
+
+       /* Create sysfs file for the FW log */
+       ret = device_create_bin_file(wl->dev, &fwlog_attr);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file fwlog");
+               goto out_hw_pg_ver;
+       }
+
+       goto out;
+
+out_hw_pg_ver:
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+out_bt_coex_state:
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+
+out:
+       return ret;
+}
+
+void wlcore_sysfs_free(struct wl1271 *wl)
+{
+       device_remove_bin_file(wl->dev, &fwlog_attr);
+
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+}
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.h b/drivers/net/wireless/ti/wlcore/sysfs.h
new file mode 100644 (file)
index 0000000..c148892
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __SYSFS_H__
+#define __SYSFS_H__
+
+int wlcore_sysfs_init(struct wl1271 *wl);
+void wlcore_sysfs_free(struct wl1271 *wl);
+
+#endif
index 004d02e..7e93fe6 100644 (file)
@@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
                         (cipher == WLAN_CIPHER_SUITE_WEP104);
 
-               if (unlikely(is_wep && wlvif->default_key != idx)) {
+               if (WARN_ON(is_wep && wlvif->default_key != idx)) {
                        ret = wl1271_set_default_wep_key(wl, wlvif, idx);
                        if (ret < 0)
                                return ret;
index 74a852e..b0b64cc 100644 (file)
@@ -36,6 +36,16 @@ config NFC_MEI_PHY
 
          If unsure, say N.
 
+config NFC_SIM
+       tristate "NFC hardware simulator driver"
+       help
+         This driver declares two virtual NFC devices supporting NFC-DEP
+         protocol. An LLCP connection can be established between them and
+         all packets sent from one device is sent back to the other, acting as
+         loopback devices.
+
+         If unsure, say N.
+
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 
index aa6bd65..be7636a 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_NFC_MICROREAD)     += microread/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
+obj-$(CONFIG_NFC_SIM)          += nfcsim.o
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 1201bdb..606bf55 100644 (file)
@@ -30,7 +30,7 @@ struct mei_nfc_hdr {
        u16 req_id;
        u32 reserved;
        u16 data_size;
-} __attribute__((packed));
+} __packed;
 
 #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
 
@@ -60,8 +60,8 @@ int nfc_mei_phy_enable(void *phy_id)
 
        r = mei_cl_enable_device(phy->device);
        if (r < 0) {
-                pr_err("MEI_PHY: Could not enable device\n");
-                return r;
+               pr_err("MEI_PHY: Could not enable device\n");
+               return r;
        }
 
        r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
index 3420d83..cdb9f6d 100644 (file)
@@ -650,7 +650,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 {
        struct microread_info *info;
        unsigned long quirks = 0;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -678,10 +678,8 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
-                                            quirks, protocols, se, llc_name,
+                                            quirks, protocols, llc_name,
                                             phy_headroom +
                                             MICROREAD_CMDS_HEADROOM,
                                             phy_tailroom +
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
new file mode 100644 (file)
index 0000000..c5c30fb
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * NFC hardware simulation driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+
+#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_VERSION "0.1"
+
+#define NFCSIM_POLL_NONE       0
+#define NFCSIM_POLL_INITIATOR  1
+#define NFCSIM_POLL_TARGET     2
+#define NFCSIM_POLL_DUAL       (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+
+struct nfcsim {
+       struct nfc_dev *nfc_dev;
+
+       struct mutex lock;
+
+       struct delayed_work recv_work;
+
+       struct sk_buff *clone_skb;
+
+       struct delayed_work poll_work;
+       u8 polling_mode;
+       u8 curr_polling_mode;
+
+       u8 shutting_down;
+
+       u8 up;
+
+       u8 initiator;
+
+       data_exchange_cb_t cb;
+       void *cb_context;
+
+       struct nfcsim *peer_dev;
+};
+
+static struct nfcsim *dev0;
+static struct nfcsim *dev1;
+
+struct workqueue_struct *wq;
+
+static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
+{
+       DEV_DBG(dev, "shutdown=%d", shutdown);
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+       dev->shutting_down = shutdown;
+       dev->cb = NULL;
+       dev_kfree_skb(dev->clone_skb);
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+       cancel_delayed_work_sync(&dev->recv_work);
+}
+
+static int nfcsim_target_found(struct nfcsim *dev)
+{
+       struct nfc_target nfc_tgt;
+
+       DEV_DBG(dev, "");
+
+       memset(&nfc_tgt, 0, sizeof(struct nfc_target));
+
+       nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+       return 0;
+}
+
+static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 1;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 0;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
+                             struct nfc_target *target,
+                             u8 comm_mode, u8 *gb, size_t gb_len)
+{
+       int rc;
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       u8 *remote_gb;
+       size_t remote_gb_len;
+
+       DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode);
+
+       mutex_lock(&peer->lock);
+
+       nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                        NFC_COMM_ACTIVE, gb, gb_len);
+
+       remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
+       if (!remote_gb) {
+               DEV_ERR(peer, "Can't get remote general bytes");
+
+               mutex_unlock(&peer->lock);
+               return -EINVAL;
+       }
+
+       mutex_unlock(&peer->lock);
+
+       mutex_lock(&dev->lock);
+
+       rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
+       if (rc) {
+               DEV_ERR(dev, "Can't set remote general bytes");
+               mutex_unlock(&dev->lock);
+               return rc;
+       }
+
+       rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE,
+                               NFC_RF_INITIATOR);
+
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       nfcsim_cleanup_dev(dev, 0);
+
+       return 0;
+}
+
+static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
+                            u32 im_protocols, u32 tm_protocols)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->polling_mode != NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Already in polling mode");
+               rc = -EBUSY;
+               goto exit;
+       }
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_INITIATOR;
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_TARGET;
+
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Unsupported polling mode");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       dev->initiator = 0;
+       dev->curr_polling_mode = NFCSIM_POLL_NONE;
+
+       queue_delayed_work(wq, &dev->poll_work, 0);
+
+       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+               tm_protocols);
+
+       rc = 0;
+exit:
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "Stop poll");
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+}
+
+static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
+                                 struct nfc_target *target, u32 protocol)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       return -ENOTSUPP;
+}
+
+static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
+                                    struct nfc_target *target)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+}
+
+static void nfcsim_wq_recv(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim,
+                                         recv_work.work);
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up || !dev->clone_skb) {
+               dev_kfree_skb(dev->clone_skb);
+               goto exit;
+       }
+
+       if (dev->initiator) {
+               if (!dev->cb) {
+                       DEV_ERR(dev, "Null recv callback");
+                       dev_kfree_skb(dev->clone_skb);
+                       goto exit;
+               }
+
+               dev->cb(dev->cb_context, dev->clone_skb, 0);
+               dev->cb = NULL;
+       } else {
+               nfc_tm_data_received(dev->nfc_dev, dev->clone_skb);
+       }
+
+exit:
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+}
+
+static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                    struct sk_buff *skb, data_exchange_cb_t cb,
+                    void *cb_context)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       int err;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up) {
+               mutex_unlock(&dev->lock);
+               err = -ENODEV;
+               goto exit;
+       }
+
+       dev->cb = cb;
+       dev->cb_context = cb_context;
+
+       mutex_unlock(&dev->lock);
+
+       mutex_lock(&peer->lock);
+
+       peer->clone_skb = skb_clone(skb, GFP_KERNEL);
+
+       if (!peer->clone_skb) {
+               DEV_ERR(dev, "skb_clone failed");
+               mutex_unlock(&peer->lock);
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* This simulates an arbitrary transmission delay between the 2 devices.
+        * If packet transmission occurs immediately between them, we have a
+        * non-stop flow of several tens of thousands SYMM packets per second
+        * and a burning cpu.
+        *
+        * TODO: Add support for a sysfs entry to control this delay.
+        */
+       queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
+
+       mutex_unlock(&peer->lock);
+
+       err = 0;
+exit:
+       dev_kfree_skb(skb);
+
+       return err;
+}
+
+static int nfcsim_im_transceive(struct nfc_dev *nfc_dev,
+                               struct nfc_target *target, struct sk_buff *skb,
+                               data_exchange_cb_t cb, void *cb_context)
+{
+       return nfcsim_tx(nfc_dev, target, skb, cb, cb_context);
+}
+
+static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL);
+}
+
+static struct nfc_ops nfcsim_nfc_ops = {
+       .dev_up = nfcsim_dev_up,
+       .dev_down = nfcsim_dev_down,
+       .dep_link_up = nfcsim_dep_link_up,
+       .dep_link_down = nfcsim_dep_link_down,
+       .start_poll = nfcsim_start_poll,
+       .stop_poll = nfcsim_stop_poll,
+       .activate_target = nfcsim_activate_target,
+       .deactivate_target = nfcsim_deactivate_target,
+       .im_transceive = nfcsim_im_transceive,
+       .tm_send = nfcsim_tm_send,
+};
+
+static void nfcsim_set_polling_mode(struct nfcsim *dev)
+{
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               dev->curr_polling_mode = NFCSIM_POLL_NONE;
+               return;
+       }
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               if (dev->polling_mode & NFCSIM_POLL_INITIATOR)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+
+               return;
+       }
+
+       if (dev->polling_mode == NFCSIM_POLL_DUAL) {
+               if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+       }
+}
+
+static void nfcsim_wq_poll(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work);
+       struct nfcsim *peer = dev->peer_dev;
+
+       /* These work items run on an ordered workqueue and are therefore
+        * serialized. So we can take both mutexes without being dead locked.
+        */
+       mutex_lock(&dev->lock);
+       mutex_lock(&peer->lock);
+
+       nfcsim_set_polling_mode(dev);
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               DEV_DBG(dev, "Not polling");
+               goto unlock;
+       }
+
+       DEV_DBG(dev, "Polling as %s",
+               dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
+               "initiator" : "target");
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+               goto sched_work;
+
+       if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) {
+               peer->polling_mode = NFCSIM_POLL_NONE;
+               dev->polling_mode = NFCSIM_POLL_NONE;
+
+               dev->initiator = 1;
+
+               nfcsim_target_found(dev);
+
+               goto unlock;
+       }
+
+sched_work:
+       /* This defines the delay for an initiator to check if the other device
+        * is polling in target mode.
+        * If the device starts in dual mode polling, it switches between
+        * initiator and target at every round.
+        * Because the wq is ordered and only 1 work item is executed at a time,
+        * we'll always have one device polling as initiator and the other as
+        * target at some point, even if both are started in dual mode.
+        */
+       queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200));
+
+unlock:
+       mutex_unlock(&peer->lock);
+       mutex_unlock(&dev->lock);
+}
+
+static struct nfcsim *nfcsim_init_dev(void)
+{
+       struct nfcsim *dev;
+       int rc = -ENOMEM;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&dev->lock);
+
+       INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv);
+       INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll);
+
+       dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops,
+                                          NFC_PROTO_NFC_DEP_MASK,
+                                          0, 0);
+       if (!dev->nfc_dev)
+               goto error;
+
+       nfc_set_drvdata(dev->nfc_dev, dev);
+
+       rc = nfc_register_device(dev->nfc_dev);
+       if (rc)
+               goto free_nfc_dev;
+
+       return dev;
+
+free_nfc_dev:
+       nfc_free_device(dev->nfc_dev);
+
+error:
+       kfree(dev);
+
+       return ERR_PTR(rc);
+}
+
+static void nfcsim_free_device(struct nfcsim *dev)
+{
+       nfc_unregister_device(dev->nfc_dev);
+
+       nfc_free_device(dev->nfc_dev);
+
+       kfree(dev);
+}
+
+int __init nfcsim_init(void)
+{
+       int rc;
+
+       /* We need an ordered wq to ensure that poll_work items are executed
+        * one at a time.
+        */
+       wq = alloc_ordered_workqueue("nfcsim", 0);
+       if (!wq) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       dev0 = nfcsim_init_dev();
+       if (IS_ERR(dev0)) {
+               rc = PTR_ERR(dev0);
+               goto exit;
+       }
+
+       dev1 = nfcsim_init_dev();
+       if (IS_ERR(dev1)) {
+               kfree(dev0);
+
+               rc = PTR_ERR(dev1);
+               goto exit;
+       }
+
+       dev0->peer_dev = dev1;
+       dev1->peer_dev = dev0;
+
+       pr_debug("NFCsim " NFCSIM_VERSION " initialized\n");
+
+       rc = 0;
+exit:
+       if (rc)
+               pr_err("Failed to initialize nfcsim driver (%d)\n",
+                      rc);
+
+       return rc;
+}
+
+void __exit nfcsim_exit(void)
+{
+       nfcsim_cleanup_dev(dev0, 1);
+       nfcsim_cleanup_dev(dev1, 1);
+
+       nfcsim_free_device(dev0);
+       nfcsim_free_device(dev1);
+
+       destroy_workqueue(wq);
+}
+
+module_init(nfcsim_init);
+module_exit(nfcsim_exit);
+
+MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
+MODULE_VERSION(NFCSIM_VERSION);
+MODULE_LICENSE("GPL");
index 3b731ac..59f95d8 100644 (file)
@@ -109,7 +109,7 @@ enum {
        NFCWILINK_FW_DOWNLOAD,
 };
 
-static int nfcwilink_send(struct sk_buff *skb);
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
 {
@@ -156,8 +156,6 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        cmd = (struct nci_vs_nfcc_info_cmd *)
                        skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
        cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
@@ -166,7 +164,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 
        drv->nfcc_info.plen = 0;
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -232,11 +230,9 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        memcpy(skb_put(skb, len), data, len);
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -371,10 +367,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return 0;
        }
 
-       skb->dev = (void *) drv->ndev;
-
        /* Forward skb to NCI core layer */
-       rc = nci_recv_frame(skb);
+       rc = nci_recv_frame(drv->ndev, skb);
        if (rc < 0) {
                nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
                return rc;
@@ -480,9 +474,8 @@ static int nfcwilink_close(struct nci_dev *ndev)
        return rc;
 }
 
-static int nfcwilink_send(struct sk_buff *skb)
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *)skb->dev;
        struct nfcwilink *drv = nci_get_drvdata(ndev);
        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
        long len;
@@ -542,7 +535,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        drv->ndev = nci_allocate_device(&nfcwilink_ops,
                                        protocols,
-                                       NFC_SE_NONE,
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
index 8f6f2ba..bfb4a4e 100644 (file)
@@ -258,7 +258,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                },
                .len = 7,
@@ -271,7 +271,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                 },
                .len = 7,
@@ -1235,7 +1235,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
 struct pn533_target_felica {
        u8 pol_res;
        u8 opcode;
-       u8 nfcid2[8];
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 pad[8];
        /* optional */
        u8 syst_code[];
@@ -1275,6 +1275,9 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
        memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
        nfc_tgt->sensf_res_len = 9;
 
+       memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE);
+       nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE;
+
        return 0;
 }
 
@@ -2084,6 +2087,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        if (comm_mode == NFC_COMM_PASSIVE)
                skb_len += PASSIVE_DATA_LEN;
 
+       if (target && target->nfcid2_len)
+               skb_len += NFC_NFCID3_MAXSIZE;
+
        skb = pn533_alloc_skb(dev, skb_len);
        if (!skb)
                return -ENOMEM;
@@ -2100,6 +2106,12 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                *next |= 1;
        }
 
+       if (target && target->nfcid2_len) {
+               memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
+                      target->nfcid2_len);
+               *next |= 2;
+       }
+
        if (gb != NULL && gb_len > 0) {
                memcpy(skb_put(skb, gb_len), gb, gb_len);
                *next |= 4; /* We have some Gi */
@@ -2489,7 +2501,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
 
        nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
 
-       print_hex_dump(KERN_ERR, "ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
                       urb->transfer_buffer, urb->transfer_buffer_length,
                       false);
 
@@ -2520,7 +2532,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
        dev->out_urb->transfer_buffer = cmd;
        dev->out_urb->transfer_buffer_length = sizeof(cmd);
 
-       print_hex_dump(KERN_ERR, "ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
                       cmd, sizeof(cmd), false);
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
@@ -2774,17 +2786,18 @@ static int pn533_probe(struct usb_interface *interface,
                goto destroy_wq;
 
        nfc_dev_info(&dev->interface->dev,
-                    "NXP PN533 firmware ver %d.%d now attached",
-                    fw_ver.ver, fw_ver.rev);
+                    "NXP PN5%02X firmware ver %d.%d now attached",
+                    fw_ver.ic, fw_ver.ver, fw_ver.rev);
 
 
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
-                                          NFC_SE_NONE,
                                           dev->ops->tx_header_len +
                                           PN533_CMD_DATAEXCH_HEAD_LEN,
                                           dev->ops->tx_tail_len);
-       if (!dev->nfc_dev)
+       if (!dev->nfc_dev) {
+               rc = -ENOMEM;
                goto destroy_wq;
+       }
 
        nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
        nfc_set_drvdata(dev->nfc_dev, dev);
index 9c5f16e..0d17da7 100644 (file)
@@ -551,20 +551,25 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
                        return -EPROTO;
                }
 
-               r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                    PN544_RF_READER_CMD_ACTIVATE_NEXT,
-                                    uid_skb->data, uid_skb->len, NULL);
-               kfree_skb(uid_skb);
-
-               r = nfc_hci_send_cmd(hdev,
+               /* Type F NFC-DEP IDm has prefix 0x01FE */
+               if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) {
+                       kfree_skb(uid_skb);
+                       r = nfc_hci_send_cmd(hdev,
                                        PN544_RF_READER_NFCIP1_INITIATOR_GATE,
                                        PN544_HCI_CMD_CONTINUE_ACTIVATION,
                                        NULL, 0, NULL);
-               if (r < 0)
-                       return r;
+                       if (r < 0)
+                               return r;
 
-               target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
-               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->hci_reader_gate =
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+               } else {
+                       r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+                                            PN544_RF_READER_CMD_ACTIVATE_NEXT,
+                                            uid_skb->data, uid_skb->len, NULL);
+                       kfree_skb(uid_skb);
+               }
        } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
                /*
                 * TODO: maybe other ISO 14443 require some kind of continue
@@ -706,12 +711,9 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                 return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
                                     PN544_RF_READER_CMD_ACTIVATE_NEXT,
                                     target->nfcid1, target->nfcid1_len, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) {
-               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                       PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
-               return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                       PN544_FELICA_RAW, NULL, 0, NULL);
+       } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK |
+                                               NFC_PROTO_FELICA_MASK)) {
+               return -EOPNOTSUPP;
        } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
                return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
                                        PN544_HCI_CMD_ATTREQUEST,
@@ -801,7 +803,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -834,10 +836,8 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
-                                            protocols, se, llc_name,
+                                            protocols, llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
index 720665c..e84cf04 100644 (file)
@@ -9,6 +9,19 @@
 
 #include "ssb_private.h"
 
+static struct resource ssb_sflash_resource = {
+       .name   = "ssb_sflash",
+       .start  = SSB_FLASH2,
+       .end    = 0,
+       .flags  = IORESOURCE_MEM | IORESOURCE_READONLY,
+};
+
+struct platform_device ssb_sflash_dev = {
+       .name           = "ssb_sflash",
+       .resource       = &ssb_sflash_resource,
+       .num_resources  = 1,
+};
+
 struct ssb_sflash_tbl_e {
        char *name;
        u32 id;
@@ -16,7 +29,7 @@ struct ssb_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -27,7 +40,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -45,7 +58,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -73,7 +86,8 @@ static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
 /* Initialize serial flash access */
 int ssb_sflash_init(struct ssb_chipcommon *cc)
 {
-       struct ssb_sflash_tbl_e *e;
+       struct ssb_sflash *sflash = &cc->dev->bus->mipscore.sflash;
+       const struct ssb_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
@@ -131,9 +145,21 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
                return -ENOTSUPP;
        }
 
+       sflash->window = SSB_FLASH2;
+       sflash->blocksize = e->blocksize;
+       sflash->numblocks = e->numblocks;
+       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);
 
+       /* Prepare platform device, but don't register it yet. It's too early,
+        * malloc (required by device_private_init) is not available yet. */
+       ssb_sflash_dev.resource[0].end = ssb_sflash_dev.resource[0].start +
+                                        sflash->size;
+       ssb_sflash_dev.dev.platform_data = sflash;
+
        pr_err("Serial flash support is not implemented yet!\n");
 
        return -ENOTSUPP;
index 812775a..e55ddf7 100644 (file)
@@ -553,6 +553,14 @@ static int ssb_devices_register(struct ssb_bus *bus)
        }
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+       if (bus->mipscore.sflash.present) {
+               err = platform_device_register(&ssb_sflash_dev);
+               if (err)
+                       pr_err("Error registering serial flash\n");
+       }
+#endif
+
        return 0;
 error:
        /* Unwind the already registered devices. */
index 4671f17..eb507a5 100644 (file)
@@ -243,6 +243,10 @@ static inline int ssb_sflash_init(struct ssb_chipcommon *cc)
 extern struct platform_device ssb_pflash_dev;
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+extern struct platform_device ssb_sflash_dev;
+#endif
+
 #ifdef CONFIG_SSB_DRIVER_EXTIF
 extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
 extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
index d826e5a..b0dc87a 100644 (file)
@@ -146,6 +146,7 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
 #define IEEE80211_MAX_RTS_THRESHOLD    2353
 #define IEEE80211_MAX_AID              2007
 #define IEEE80211_MAX_TIM_LEN          251
+#define IEEE80211_MAX_MESH_PEERINGS    63
 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
    6.2.1.1.2.
 
index afe79d4..6535e47 100644 (file)
@@ -20,6 +20,18 @@ struct ssb_pflash {
        u32 window_size;
 };
 
+#ifdef CONFIG_SSB_SFLASH
+struct ssb_sflash {
+       bool present;
+       u32 window;
+       u32 blocksize;
+       u16 numblocks;
+       u32 size;
+
+       void *priv;
+};
+#endif
+
 struct ssb_mipscore {
        struct ssb_device *dev;
 
@@ -27,6 +39,9 @@ struct ssb_mipscore {
        struct ssb_serial_port serial_ports[4];
 
        struct ssb_pflash pflash;
+#ifdef CONFIG_SSB_SFLASH
+       struct ssb_sflash sflash;
+#endif
 };
 
 extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
index 6dd1959..6a43c34 100644 (file)
@@ -1124,6 +1124,9 @@ struct bss_parameters {
  *     setting for new peer links.
  * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
  *     after transmitting its beacon.
+ * @plink_timeout: If no tx activity is seen from a STA we've established
+ *     peering with for longer than this time (in seconds), then remove it
+ *     from the STA's list of peers.  Default is 30 minutes.
  */
 struct mesh_config {
        u16 dot11MeshRetryTimeout;
@@ -1153,6 +1156,7 @@ struct mesh_config {
        u16 dot11MeshHWMPconfirmationInterval;
        enum nl80211_mesh_power_mode power_mode;
        u16 dot11MeshAwakeWindowDuration;
+       u32 plink_timeout;
 };
 
 /**
@@ -1172,6 +1176,7 @@ struct mesh_config {
  * @dtim_period: DTIM period to use
  * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
+ * @basic_rates: basic rates to use when creating the mesh
  *
  * These parameters are fixed when the mesh is created.
  */
@@ -1191,6 +1196,7 @@ struct mesh_setup {
        u8 dtim_period;
        u16 beacon_interval;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       u32 basic_rates;
 };
 
 /**
@@ -2654,7 +2660,7 @@ struct wiphy {
        u32 hw_version;
 
 #ifdef CONFIG_PM
-       struct wiphy_wowlan_support wowlan;
+       const struct wiphy_wowlan_support *wowlan;
        struct cfg80211_wowlan *wowlan_config;
 #endif
 
@@ -2853,7 +2859,7 @@ struct cfg80211_cached_keys;
  * @current_bss: (private) Used by the internal configuration code
  * @channel: (private) Used by the internal configuration code to track
  *     the user-set AP, monitor and WDS channel
- * @preset_chan: (private) Used by the internal configuration code to
+ * @preset_chandef: (private) Used by the internal configuration code to
  *     track the channel to be used for AP later
  * @bssid: (private) Used by the internal configuration code
  * @ssid: (private) Used by the internal configuration code
@@ -2875,6 +2881,15 @@ struct cfg80211_cached_keys;
  * @p2p_started: true if this is a P2P Device that has been started
  * @cac_started: true if DFS channel availability check has been started
  * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @ps: powersave mode is enabled
+ * @ps_timeout: dynamic powersave timeout
+ * @ap_unexpected_nlportid: (private) netlink port ID of application
+ *     registered for unexpected class 3 frames (AP mode)
+ * @conn: (private) cfg80211 software SME connection state machine data
+ * @connect_keys: (private) keys to set after connection is established
+ * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @event_list: (private) list for internal event processing
+ * @event_lock: (private) lock for event list
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -2898,11 +2913,6 @@ struct wireless_dev {
        /* currently used for IBSS and SME - might be rearranged later */
        u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len, mesh_id_len, mesh_id_up_len;
-       enum {
-               CFG80211_SME_IDLE,
-               CFG80211_SME_CONNECTING,
-               CFG80211_SME_CONNECTED,
-       } sme_state;
        struct cfg80211_conn *conn;
        struct cfg80211_cached_keys *connect_keys;
 
@@ -3432,59 +3442,66 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
 /**
- * cfg80211_send_rx_auth - notification of processed authentication
+ * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
  * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever an authentication has been processed in
- * station mode. The driver is required to call either this function or
- * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth()
- * call. This function may sleep. The caller must hold the corresponding wdev's
- * mutex.
+ * This function is called whenever an authentication, disassociation or
+ * deauthentication frame has been received and processed in station mode.
+ * After being asked to authenticate via cfg80211_ops::auth() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * While connected, the driver must calls this for received and processed
+ * disassociation and deauthentication frames. If the frame couldn't be used
+ * because it was unprotected, the driver must call the function
+ * cfg80211_rx_unprot_mlme_mgmt() instead.
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_auth_timeout - notification of timed out authentication
+ * cfg80211_auth_timeout - notification of timed out authentication
  * @dev: network device
  * @addr: The MAC address of the device with which the authentication timed out
  *
  * This function may sleep. The caller must hold the corresponding wdev's
  * mutex.
  */
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
 
 /**
- * cfg80211_send_rx_assoc - notification of processed association
+ * cfg80211_rx_assoc_resp - notification of processed association response
  * @dev: network device
- * @bss: the BSS struct association was requested for, the struct reference
- *     is owned by cfg80211 after this call
- * @buf: (re)association response frame (header + body)
+ * @bss: the BSS that association was requested with, ownership of the pointer
+ *     moves to cfg80211 in this call
+ * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a (re)association response has been
- * processed in station mode. The driver is required to call either this
- * function or cfg80211_send_assoc_timeout() to indicate the result of
- * cfg80211_ops::assoc() call. This function may sleep. The caller must hold
- * the corresponding wdev's mutex.
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev,
+                           struct cfg80211_bss *bss,
                            const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_assoc_timeout - notification of timed out association
+ * cfg80211_assoc_timeout - notification of timed out association
  * @dev: network device
  * @addr: The MAC address of the device with which the association timed out
  *
  * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr);
 
 /**
- * cfg80211_send_deauth - notification of processed deauthentication
+ * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
  * @dev: network device
- * @buf: deauthentication frame (header + body)
+ * @buf: 802.11 frame (header + body)
  * @len: length of the frame data
  *
  * This function is called whenever deauthentication has been processed in
@@ -3492,46 +3509,20 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
  * locally generated ones. This function may sleep. The caller must hold the
  * corresponding wdev's mutex.
  */
-void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
- * cfg80211_send_disassoc - notification of processed disassociation
- * @dev: network device
- * @buf: disassociation response frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever disassociation has been processed in
- * station mode. This includes both received disassociation frames and locally
- * generated ones. This function may sleep. The caller must hold the
- * corresponding wdev's mutex.
- */
-void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_unprot_deauth - notification of unprotected deauthentication
+ * cfg80211_rx_unprot_mlme_mgmt - notification of unprotected mlme mgmt frame
  * @dev: network device
  * @buf: deauthentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a received Deauthentication frame has been
- * dropped in station mode because of MFP being used but the Deauthentication
- * frame was not protected. This function may sleep.
- */
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len);
-
-/**
- * cfg80211_send_unprot_disassoc - notification of unprotected disassociation
- * @dev: network device
- * @buf: disassociation frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever a received Disassociation frame has been
- * dropped in station mode because of MFP being used but the Disassociation
+ * This function is called whenever a received deauthentication or dissassoc
+ * frame has been dropped in station mode because of MFP being used but the
  * frame was not protected. This function may sleep.
  */
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len);
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev,
+                                 const u8 *buf, size_t len);
 
 /**
  * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
index 1f0014b..a405a7a 100644 (file)
@@ -217,8 +217,8 @@ struct ieee80211_chanctx_conf {
  * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
  * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
  *     changed (currently only in P2P client mode, GO mode will be later)
- * @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when
- *     it becomes valid, managed mode only)
+ * @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
+ *     currently dtim_period only is under consideration.
  * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
  *     note that this is only called when it changes after the channel
  *     context had been assigned.
@@ -244,7 +244,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_PS                  = 1<<17,
        BSS_CHANGED_TXPOWER             = 1<<18,
        BSS_CHANGED_P2P_PS              = 1<<19,
-       BSS_CHANGED_DTIM_PERIOD         = 1<<20,
+       BSS_CHANGED_BEACON_INFO         = 1<<20,
        BSS_CHANGED_BANDWIDTH           = 1<<21,
 
        /* when adding here, make sure to change ieee80211_reconfig */
@@ -288,7 +288,7 @@ enum ieee80211_rssi_event {
  *     IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
  * @dtim_period: num of beacons before the next DTIM, for beaconing,
  *     valid in station mode only if after the driver was notified
- *     with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then.
+ *     with the %BSS_CHANGED_BEACON_INFO flag, will be non-zero then.
  * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old
  *     as it may have been received during scanning long ago). If the
  *     HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
@@ -460,6 +460,8 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
  *     would be fragmented by size (this is optional, only used for
  *     monitor injection).
+ * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll
+ *     frame (PS-Poll or uAPSD).
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *      forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -495,6 +497,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_STATUS_EOSP                = BIT(28),
        IEEE80211_TX_CTL_USE_MINRATE            = BIT(29),
        IEEE80211_TX_CTL_DONTFRAG               = BIT(30),
+       IEEE80211_TX_CTL_PS_RESPONSE            = BIT(31),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT            23
index b87a169..0af851c 100644 (file)
@@ -59,8 +59,10 @@ struct nfc_hci_ops {
                              struct nfc_target *target);
        int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                              struct sk_buff *skb);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_hci_dev *hdev, const char *firmware_name);
+       int (*discover_se)(struct nfc_hci_dev *dev);
+       int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
 };
 
 /* Pipes */
@@ -152,7 +154,6 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
index 5bc0c46..99fc1f3 100644 (file)
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2013 Intel Corporation. All rights reserved.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -66,7 +67,7 @@ struct nci_dev;
 struct nci_ops {
        int (*open)(struct nci_dev *ndev);
        int (*close)(struct nci_dev *ndev);
-       int (*send)(struct sk_buff *skb);
+       int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
@@ -147,13 +148,12 @@ struct nci_dev {
 /* ----- NCI Devices ----- */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 void nci_free_device(struct nci_dev *ndev);
 int nci_register_device(struct nci_dev *ndev);
 void nci_unregister_device(struct nci_dev *ndev);
-int nci_recv_frame(struct sk_buff *skb);
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
                                            unsigned int len,
@@ -202,4 +202,56 @@ void nci_req_complete(struct nci_dev *ndev, int result);
 /* ----- NCI status code ----- */
 int nci_to_errno(__u8 code);
 
+/* ----- NCI over SPI acknowledge modes ----- */
+#define NCI_SPI_CRC_DISABLED   0x00
+#define NCI_SPI_CRC_ENABLED    0x01
+
+/* ----- NCI SPI structures ----- */
+struct nci_spi_dev;
+
+struct nci_spi_ops {
+       int (*open)(struct nci_spi_dev *ndev);
+       int (*close)(struct nci_spi_dev *ndev);
+       void (*assert_int)(struct nci_spi_dev *ndev);
+       void (*deassert_int)(struct nci_spi_dev *ndev);
+};
+
+struct nci_spi_dev {
+       struct nci_dev          *nci_dev;
+       struct spi_device       *spi;
+       struct nci_spi_ops      *ops;
+
+       unsigned int            xfer_udelay;    /* microseconds delay between
+                                                 transactions */
+       u8                      acknowledge_mode;
+
+       struct completion       req_completion;
+       u8                      req_result;
+
+       void                    *driver_data;
+};
+
+/* ----- NCI SPI Devices ----- */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay);
+void nci_spi_free_device(struct nci_spi_dev *ndev);
+int nci_spi_register_device(struct nci_spi_dev *ndev);
+void nci_spi_unregister_device(struct nci_spi_dev *ndev);
+int nci_spi_recv_frame(struct nci_spi_dev *ndev);
+
+static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
+                                           void *data)
+{
+       ndev->driver_data = data;
+}
+
+static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
+{
+       return ndev->driver_data;
+}
+
 #endif /* __NCI_CORE_H */
index 5eb80bb..0e353f1 100644 (file)
@@ -68,8 +68,12 @@ struct nfc_ops {
                             void *cb_context);
        int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_dev *dev, const char *firmware_name);
+
+       /* Secure Element API */
+       int (*discover_se)(struct nfc_dev *dev);
+       int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -83,6 +87,8 @@ struct nfc_target {
        u8 sel_res;
        u8 nfcid1_len;
        u8 nfcid1[NFC_NFCID1_MAXSIZE];
+       u8 nfcid2_len;
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 sensb_res_len;
        u8 sensb_res[NFC_SENSB_RES_MAXSIZE];
        u8 sensf_res_len;
@@ -91,6 +97,23 @@ struct nfc_target {
        u8 logical_idx;
 };
 
+/**
+ * nfc_se - A structure for NFC accessible secure elements.
+ *
+ * @idx: The secure element index. User space will enable or
+ *       disable a secure element by its index.
+ * @type: The secure element type. It can be SE_UICC or
+ *        SE_EMBEDDED.
+ * @state: The secure element state, either enabled or disabled.
+ *
+ */
+struct nfc_se {
+       struct list_head list;
+       u32 idx;
+       u16 type;
+       u16 state;
+};
+
 struct nfc_genl_data {
        u32 poll_req_portid;
        struct mutex genl_data_mutex;
@@ -104,6 +127,7 @@ struct nfc_dev {
        int targets_generation;
        struct device dev;
        bool dev_up;
+       bool fw_upload_in_progress;
        u8 rf_mode;
        bool polling;
        struct nfc_target *active_target;
@@ -111,8 +135,7 @@ struct nfc_dev {
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
-       u32 supported_se;
-       u32 active_se;
+       struct list_head secure_elements;
 
        int tx_headroom;
        int tx_tailroom;
@@ -132,7 +155,6 @@ extern struct class nfc_class;
 
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 
@@ -216,4 +238,7 @@ int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 
 void nfc_driver_failure(struct nfc_dev *dev, int err);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __NET_NFC_H */
index 7c6f627..caed0f3 100644 (file)
@@ -69,6 +69,8 @@
  *     starting a poll from a device which has a secure element enabled means
  *     we want to do SE based card emulation.
  * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
+ * @NFC_CMD_FW_UPLOAD: Request to Load/flash firmware, or event to inform that
+ *     some firmware was loaded
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -92,6 +94,9 @@ enum nfc_commands {
        NFC_CMD_DISABLE_SE,
        NFC_CMD_LLC_SDREQ,
        NFC_EVENT_LLC_SDRES,
+       NFC_CMD_FW_UPLOAD,
+       NFC_EVENT_SE_ADDED,
+       NFC_EVENT_SE_REMOVED,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -121,6 +126,9 @@ enum nfc_commands {
  * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
  * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
  * @NFC_ATTR_SE: Available Secure Elements
+ * @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)
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -143,6 +151,9 @@ enum nfc_attrs {
        NFC_ATTR_LLC_PARAM_MIUX,
        NFC_ATTR_SE,
        NFC_ATTR_LLC_SDP,
+       NFC_ATTR_FIRMWARE_NAME,
+       NFC_ATTR_SE_INDEX,
+       NFC_ATTR_SE_TYPE,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
@@ -159,9 +170,12 @@ enum nfc_sdp_attr {
 
 #define NFC_DEVICE_NAME_MAXSIZE 8
 #define NFC_NFCID1_MAXSIZE 10
+#define NFC_NFCID2_MAXSIZE 8
+#define NFC_NFCID3_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
 #define NFC_SENSF_RES_MAXSIZE 18
 #define NFC_GB_MAXSIZE        48
+#define NFC_FIRMWARE_NAME_MAXSIZE 32
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL                1
@@ -191,10 +205,12 @@ enum nfc_sdp_attr {
 #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
 
 /* NFC Secure Elements */
-#define NFC_SE_NONE     0x0
 #define NFC_SE_UICC     0x1
 #define NFC_SE_EMBEDDED 0x2
 
+#define NFC_SE_DISABLED 0x0
+#define NFC_SE_ENABLED  0x1
+
 struct sockaddr_nfc {
        sa_family_t sa_family;
        __u32 dev_idx;
index 5920715..ca6facf 100644 (file)
@@ -2577,6 +2577,10 @@ enum nl80211_mesh_power_mode {
  *
  * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
  *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *     established peering with for longer than this time (in seconds), then
+ *     remove it from the STA's list of peers.  Default is 30 minutes.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2608,6 +2612,7 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
        NL80211_MESHCONF_POWER_MODE,
        NL80211_MESHCONF_AWAKE_WINDOW,
+       NL80211_MESHCONF_PLINK_TIMEOUT,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -3579,6 +3584,10 @@ enum nl80211_ap_sme_features {
  *     Peering Management entity which may be implemented by registering for
  *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
  *     still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *     interface. An active monitor interface behaves like a normal monitor
+ *     interface, but gets added to the driver. It ensures that incoming
+ *     unicast packets directed at the configured interface address get ACKed.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
index a1c6e1c..082f270 100644 (file)
@@ -1765,6 +1765,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        /* mcast rate setting in Mesh Node */
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
+       sdata->vif.bss_conf.basic_rates = setup->basic_rates;
 
        sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
        sdata->vif.bss_conf.dtim_period = setup->dtim_period;
@@ -1877,6 +1878,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
        if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
                conf->dot11MeshAwakeWindowDuration =
                        nconf->dot11MeshAwakeWindowDuration;
+       if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
+               conf->plink_timeout = nconf->plink_timeout;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
 }
@@ -2844,6 +2847,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EOPNOTSUPP;
        }
 
+       /* configurations requiring offchan cannot work if no channel has been
+        * specified
+        */
+       if (need_offchan && !chan)
+               return -EINVAL;
+
        mutex_lock(&local->mtx);
 
        /* Check if the operating channel is the requested channel */
@@ -2853,10 +2862,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 
-               if (chanctx_conf)
-                       need_offchan = chan != chanctx_conf->def.chan;
-               else
+               if (chanctx_conf) {
+                       need_offchan = chan && (chan != chanctx_conf->def.chan);
+               } else if (!chan) {
+                       ret = -EINVAL;
+                       rcu_read_unlock();
+                       goto out_unlock;
+               } else {
                        need_offchan = true;
+               }
                rcu_read_unlock();
        }
 
index 923e177..f97cd9d 100644 (file)
@@ -366,7 +366,7 @@ struct ieee80211_mgd_assoc_data {
        u8 ssid_len;
        u8 supp_rates_len;
        bool wmm, uapsd;
-       bool have_beacon, need_beacon;
+       bool need_beacon;
        bool synced;
        bool timeout_started;
 
@@ -404,6 +404,7 @@ struct ieee80211_if_managed {
 
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
+       bool have_beacon;
        u8 dtim_period;
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
                                 driver_smps_mode; /* smps mode request */
index 1998f14..626c83c 100644 (file)
@@ -686,8 +686,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                return -EINVAL;
 
 #ifdef CONFIG_PM
-       if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) &&
-           (!local->ops->suspend || !local->ops->resume))
+       if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
                return -EINVAL;
 #endif
 
index b3d1fdd..6c33af4 100644 (file)
@@ -274,8 +274,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = ifmsh->mesh_auth_id;
        /* Mesh Formation Info - number of neighbors */
        neighbors = atomic_read(&ifmsh->estab_plinks);
-       /* Number of neighbor mesh STAs or 15 whichever is smaller */
-       neighbors = (neighbors > 15) ? 15 : neighbors;
+       neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
        *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
@@ -576,13 +575,11 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        u32 changed;
 
-       ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
+       ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
        mesh_path_expire(sdata);
 
        changed = mesh_accept_plinks_update(sdata);
-       sdata_lock(sdata);
        ieee80211_mbss_info_change_notify(sdata, changed);
-       sdata_unlock(sdata);
 
        mod_timer(&ifmsh->housekeeping_timer,
                  round_jiffies(jiffies +
@@ -741,9 +738,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
                      BSS_CHANGED_HT |
                      BSS_CHANGED_BASIC_RATES |
                      BSS_CHANGED_BEACON_INT;
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_supported_band *sband =
-                                       sdata->local->hw.wiphy->bands[band];
 
        local->fif_other_bss++;
        /* mesh ifaces must set allmulti to forward mcast traffic */
@@ -761,7 +755,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.ht_operation_mode =
                                ifmsh->mshcfg.ht_opmode;
        sdata->vif.bss_conf.enable_beacon = true;
-       sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sband);
 
        changed |= ieee80211_mps_local_status_update(sdata);
 
@@ -789,12 +782,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.enable_beacon = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
-       sdata_lock(sdata);
        bcn = rcu_dereference_protected(ifmsh->beacon,
                                        lockdep_is_held(&sdata->wdev.mtx));
        rcu_assign_pointer(ifmsh->beacon, NULL);
        kfree_rcu(bcn, rcu_head);
-       sdata_unlock(sdata);
 
        /* flush STAs and mpaths on this iface */
        sta_info_flush(sdata);
@@ -807,14 +798,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_timer);
-       /*
-        * If the timer fired while we waited for it, it will have
-        * requeued the work. Now the work will be running again
-        * but will not rearm the timer again because it checks
-        * whether the interface is running, which, at this point,
-        * it no longer is.
-        */
-       cancel_work_sync(&sdata->work);
 
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
@@ -955,6 +938,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *mgmt;
        u16 stype;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        rx_status = IEEE80211_SKB_RXCB(skb);
        mgmt = (struct ieee80211_mgmt *) skb->data;
        stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
@@ -972,12 +961,20 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
                break;
        }
+out:
+       sdata_unlock(sdata);
 }
 
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        if (ifmsh->preq_queue_len &&
            time_after(jiffies,
                       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
@@ -997,6 +994,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 
        if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
                mesh_sync_adjust_tbtt(sdata);
+
+out:
+       sdata_unlock(sdata);
 }
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
index da15877..01a28bc 100644 (file)
@@ -188,7 +188,6 @@ struct mesh_rmc {
        u32 idx_mask;
 };
 
-#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
 #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
 
 #define MESH_PATH_EXPIRE (600 * HZ)
@@ -324,14 +323,14 @@ static inline
 u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_inc(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline
 u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_dec(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
index 6c4da99..09bebed 100644 (file)
@@ -517,9 +517,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
        ieee80211_mps_frame_release(sta, elems);
 out:
        rcu_read_unlock();
-       sdata_lock(sdata);
        ieee80211_mbss_info_change_notify(sdata, changed);
-       sdata_unlock(sdata);
 }
 
 static void mesh_plink_timer(unsigned long data)
@@ -1070,9 +1068,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_unlock();
 
-       if (changed) {
-               sdata_lock(sdata);
+       if (changed)
                ieee80211_mbss_info_change_notify(sdata, changed);
-               sdata_unlock(sdata);
-       }
 }
index 118540b..9e49f55 100644 (file)
@@ -880,6 +880,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
@@ -1356,7 +1360,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
                          IEEE80211_STA_CONNECTION_POLL))
                return false;
 
-       if (!sdata->vif.bss_conf.dtim_period)
+       if (!mgd->have_beacon)
                return false;
 
        rcu_read_lock();
@@ -1767,7 +1771,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_led_assoc(local, 1);
 
-       if (sdata->u.mgd.assoc_data->have_beacon) {
+       if (sdata->u.mgd.have_beacon) {
                /*
                 * If the AP is buggy we may get here with no DTIM period
                 * known, so assume it's 1 which is the only safe assumption
@@ -1775,7 +1779,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                 * probably just won't work at all.
                 */
                bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
-               bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
+               bss_info_changed |= BSS_CHANGED_BEACON_INFO;
        } else {
                bss_conf->dtim_period = 0;
        }
@@ -1899,6 +1903,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 
        sdata->vif.bss_conf.dtim_period = 0;
+       ifmgd->have_beacon = false;
 
        ifmgd->flags = 0;
        ieee80211_vif_release_channel(sdata);
@@ -2151,7 +2156,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
        sdata_unlock(sdata);
 }
 
@@ -2298,7 +2304,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                sdata_info(sdata, "%pM denied authentication (status %d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_auth_data(sdata, false);
-               cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
                return;
        }
 
@@ -2333,7 +2339,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                 * Report auth frame to user space for processing since another
                 * round of Authentication frames is still needed.
                 */
-               cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
                return;
        }
 
@@ -2350,7 +2356,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&sdata->local->sta_mtx);
 
-       cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
        return;
  out_err:
        mutex_unlock(&sdata->local->sta_mtx);
@@ -2383,7 +2389,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 
@@ -2409,7 +2415,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2780,7 +2786,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false);
                        cfg80211_put_bss(sdata->local->hw.wiphy, bss);
-                       cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
+                       cfg80211_assoc_timeout(sdata->dev, mgmt->bssid);
                        return;
                }
                sdata_info(sdata, "associated\n");
@@ -2793,7 +2799,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                ieee80211_destroy_assoc_data(sdata, true);
        }
 
-       cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, len);
+       cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -2805,24 +2811,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
-       bool need_ps = false;
 
        sdata_assert_lock(sdata);
 
-       if ((sdata->u.mgd.associated &&
-            ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
-           (sdata->u.mgd.assoc_data &&
-            ether_addr_equal(mgmt->bssid,
-                             sdata->u.mgd.assoc_data->bss->bssid))) {
-               /* not previously set so we may need to recalc */
-               need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
-
-               if (elems->tim && !elems->parse_error) {
-                       const struct ieee80211_tim_ie *tim_ie = elems->tim;
-                       sdata->u.mgd.dtim_period = tim_ie->dtim_period;
-               }
-       }
-
        if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
                                                      rx_status->band);
@@ -2843,12 +2834,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
            !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
                return;
 
-       if (need_ps) {
-               mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
-               mutex_unlock(&local->iflist_mtx);
-       }
-
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         elems, true);
 
@@ -2962,7 +2947,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                       len - baselen, false, &elems);
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
-               ifmgd->assoc_data->have_beacon = true;
+               if (elems.tim && !elems.parse_error) {
+                       const struct ieee80211_tim_ie *tim_ie = elems.tim;
+                       ifmgd->dtim_period = tim_ie->dtim_period;
+               }
+               ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
                if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
                        sdata->vif.bss_conf.sync_tsf =
@@ -3144,7 +3133,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * If we haven't had a beacon before, tell the driver about the
         * DTIM period (and beacon timing if desired) now.
         */
-       if (!bss_conf->dtim_period) {
+       if (!ifmgd->have_beacon) {
                /* a few bogus AP send dtim_period = 0 or no TIM IE */
                if (elems.tim)
                        bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
@@ -3163,7 +3152,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                sdata->vif.bss_conf.sync_dtim_count = 0;
                }
 
-               changed |= BSS_CHANGED_DTIM_PERIOD;
+               changed |= BSS_CHANGED_BEACON_INFO;
+               ifmgd->have_beacon = true;
+
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local, -1);
+               mutex_unlock(&local->iflist_mtx);
+
                ieee80211_recalc_ps_vif(sdata);
        }
 
@@ -3186,8 +3181,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
-               cfg80211_send_deauth(sdata->dev, deauth_buf,
-                                    sizeof(deauth_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
+                                     sizeof(deauth_buf));
                return;
        }
 
@@ -3305,7 +3300,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               tx, frame_buf);
 
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 }
 
 static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3496,15 +3492,14 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                        ieee80211_destroy_auth_data(sdata, false);
 
-                       cfg80211_send_auth_timeout(sdata->dev, bssid);
+                       cfg80211_auth_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
                run_again(sdata, ifmgd->auth_data->timeout);
 
        if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
            time_after(jiffies, ifmgd->assoc_data->timeout)) {
-               if ((ifmgd->assoc_data->need_beacon &&
-                    !ifmgd->assoc_data->have_beacon) ||
+               if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
                    ieee80211_do_assoc(sdata)) {
                        u8 bssid[ETH_ALEN];
 
@@ -3512,7 +3507,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                        ieee80211_destroy_assoc_data(sdata, false);
 
-                       cfg80211_send_assoc_timeout(sdata->dev, bssid);
+                       cfg80211_assoc_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
                run_again(sdata, ifmgd->assoc_data->timeout);
@@ -4061,8 +4056,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4124,8 +4119,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4272,6 +4267,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->assoc_data = assoc_data;
        ifmgd->dtim_period = 0;
+       ifmgd->have_beacon = false;
 
        err = ieee80211_prep_connection(sdata, req->bss, true);
        if (err)
@@ -4303,7 +4299,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        ifmgd->dtim_period = tim->dtim_period;
                        dtim_count = tim->dtim_count;
                }
-               assoc_data->have_beacon = true;
+               ifmgd->have_beacon = true;
                assoc_data->timeout = jiffies;
                assoc_data->timeout_started = true;
 
@@ -4378,8 +4374,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
  out:
        if (report_frame)
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    IEEE80211_DEAUTH_FRAME_LEN);
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
@@ -4409,8 +4405,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                               req->reason_code, !req->local_state_change,
                               frame_buf);
 
-       cfg80211_send_disassoc(sdata->dev, frame_buf,
-                              IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
index bdd7b4a..23dbcfc 100644 (file)
@@ -1747,27 +1747,21 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
                if (unlikely(!ieee80211_has_protected(fc) &&
                             ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
                             rx->key)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /* BIP does not use Protected field, so need to check MMIE */
                if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
                             ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /*
index a04c567..b429798 100644 (file)
@@ -1132,6 +1132,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
         * ends the poll/service period.
         */
        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                      IEEE80211_TX_CTL_PS_RESPONSE |
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
@@ -1269,7 +1270,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                         * STA may still remain is PS mode after this frame
                         * exchange.
                         */
-                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                                      IEEE80211_TX_CTL_PS_RESPONSE;
 
                        /*
                         * Use MoreData flag to indicate whether there are
index 41c28b9..bd12fc5 100644 (file)
@@ -297,6 +297,9 @@ struct sta_ampdu_mlme {
  * @rcu_head: RCU head used for freeing this station struct
  * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
  *     taken from HT/VHT capabilities or VHT operating mode notification
+ * @chains: chains ever used for RX from this station
+ * @chain_signal_last: last signal (per chain)
+ * @chain_signal_avg: signal average (per chain)
  */
 struct sta_info {
        /* General information, mostly static */
index 34be933..4105d0c 100644 (file)
@@ -1790,12 +1790,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
-               if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
-                       /* Do not send frames with mesh_ttl == 0 */
-                       sdata->u.mesh.mshstats.dropped_frames_ttl++;
-                       goto fail_rcu;
-               }
-
                if (!is_multicast_ether_addr(skb->data)) {
                        struct sta_info *next_hop;
                        bool mpp_lookup = true;
index c75d3db..2265445 100644 (file)
@@ -1584,8 +1584,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                   BSS_CHANGED_ARP_FILTER |
                                   BSS_CHANGED_PS;
 
-                       if (sdata->u.mgd.dtim_period)
-                               changed |= BSS_CHANGED_DTIM_PERIOD;
+                       /* Re-send beacon info report to the driver */
+                       if (sdata->u.mgd.have_beacon)
+                               changed |= BSS_CHANGED_BEACON_INFO;
 
                        sdata_lock(sdata);
                        ieee80211_bss_info_change_notify(sdata, changed);
index 40d2527..dc96a83 100644 (file)
@@ -44,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex);
 /* NFC device ID bitmap */
 static DEFINE_IDA(nfc_index_ida);
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name)
+{
+       int rc = 0;
+
+       pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->dev_up) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->fw_upload) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       dev->fw_upload_in_progress = true;
+       rc = dev->ops->fw_upload(dev, firmware_name);
+       if (rc)
+               dev->fw_upload_in_progress = false;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       dev->fw_upload_in_progress = false;
+
+       return nfc_genl_fw_upload_done(dev, firmware_name);
+}
+EXPORT_SYMBOL(nfc_fw_upload_done);
+
 /**
  * nfc_dev_up - turn on the NFC device
  *
@@ -69,6 +110,11 @@ int nfc_dev_up(struct nfc_dev *dev)
                goto error;
        }
 
+       if (dev->fw_upload_in_progress) {
+               rc = -EBUSY;
+               goto error;
+       }
+
        if (dev->dev_up) {
                rc = -EALREADY;
                goto error;
@@ -80,6 +126,13 @@ int nfc_dev_up(struct nfc_dev *dev)
        if (!rc)
                dev->dev_up = true;
 
+       /* We have to enable the device before discovering SEs */
+       if (dev->ops->discover_se) {
+               rc = dev->ops->discover_se(dev);
+               if (!rc)
+                       pr_warn("SE discovery failed\n");
+       }
+
 error:
        device_unlock(&dev->dev);
        return rc;
@@ -475,6 +528,108 @@ error:
        return rc;
 }
 
+static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx)
+                       return se;
+
+       return NULL;
+}
+
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->polling) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_ENABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->enable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_DISABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->disable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
 int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
 {
        pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
@@ -707,14 +862,79 @@ inline void nfc_driver_failure(struct nfc_dev *dev, int err)
 }
 EXPORT_SYMBOL(nfc_driver_failure);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       se = find_se(dev, se_idx);
+       if (se)
+               return -EALREADY;
+
+       se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
+       if (!se)
+               return -ENOMEM;
+
+       se->idx = se_idx;
+       se->type = type;
+       se->state = NFC_SE_DISABLED;
+       INIT_LIST_HEAD(&se->list);
+
+       list_add(&se->list, &dev->secure_elements);
+
+       rc = nfc_genl_se_added(dev, se_idx, type);
+       if (rc < 0) {
+               list_del(&se->list);
+               kfree(se);
+
+               return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_add_se);
+
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx) {
+                       rc = nfc_genl_se_removed(dev, se_idx);
+                       if (rc < 0)
+                               return rc;
+
+                       list_del(&se->list);
+                       kfree(se);
+
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(nfc_remove_se);
+
 static void nfc_release(struct device *d)
 {
        struct nfc_dev *dev = to_nfc_dev(d);
+       struct nfc_se *se, *n;
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+                       nfc_genl_se_removed(dev, se->idx);
+                       list_del(&se->list);
+                       kfree(se);
+       }
+
        kfree(dev);
 }
 
@@ -786,7 +1006,6 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nfc_dev *dev;
@@ -804,10 +1023,9 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        dev->ops = ops;
        dev->supported_protocols = supported_protocols;
-       dev->supported_se = supported_se;
-       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
+       INIT_LIST_HEAD(&dev->secure_elements);
 
        nfc_genl_data_init(&dev->genl_data);
 
index 91020b2..7b1c186 100644 (file)
@@ -570,21 +570,21 @@ static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_up)
-               return hdev->ops->dep_link_up(hdev, target, comm_mode,
-                                               gb, gb_len);
+       if (!hdev->ops->dep_link_up)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_up(hdev, target, comm_mode,
+                                     gb, gb_len);
 }
 
 static int hci_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_down)
-               return hdev->ops->dep_link_down(hdev);
+       if (!hdev->ops->dep_link_down)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_down(hdev);
 }
 
 static int hci_activate_target(struct nfc_dev *nfc_dev,
@@ -673,12 +673,12 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->tm_send)
-               return hdev->ops->tm_send(hdev, skb);
-
-       kfree_skb(skb);
+       if (!hdev->ops->tm_send) {
+               kfree_skb(skb);
+               return -ENOTSUPP;
+       }
 
-       return -ENOTSUPP;
+       return hdev->ops->tm_send(hdev, skb);
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -686,8 +686,38 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->check_presence)
-               return hdev->ops->check_presence(hdev, target);
+       if (!hdev->ops->check_presence)
+               return 0;
+
+       return hdev->ops->check_presence(hdev, target);
+}
+
+static int hci_discover_se(struct nfc_dev *nfc_dev)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->discover_se)
+               return hdev->ops->discover_se(hdev);
+
+       return 0;
+}
+
+static int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->enable_se)
+               return hdev->ops->enable_se(hdev, se_idx);
+
+       return 0;
+}
+
+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 0;
 }
@@ -779,6 +809,16 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static int hci_fw_upload(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (!hdev->ops->fw_upload)
+               return -ENOTSUPP;
+
+       return hdev->ops->fw_upload(hdev, firmware_name);
+}
+
 static struct nfc_ops hci_nfc_ops = {
        .dev_up = hci_dev_up,
        .dev_down = hci_dev_down,
@@ -791,13 +831,16 @@ static struct nfc_ops hci_nfc_ops = {
        .im_transceive = hci_transceive,
        .tm_send = hci_tm_send,
        .check_presence = hci_check_presence,
+       .fw_upload = hci_fw_upload,
+       .discover_se = hci_discover_se,
+       .enable_se = hci_enable_se,
+       .disable_se = hci_disable_se,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -823,7 +866,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                return NULL;
        }
 
-       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
+       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
index ff8c434..f4d48b5 100644 (file)
@@ -19,6 +19,8 @@
 
 enum llcp_state {
        LLCP_CONNECTED = 1, /* wait_for_packet() wants that */
+       LLCP_CONNECTING,
+       LLCP_DISCONNECTING,
        LLCP_CLOSED,
        LLCP_BOUND,
        LLCP_LISTEN,
@@ -246,7 +248,6 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
 void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
 void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
index c1b23ee..1017894 100644 (file)
@@ -339,7 +339,7 @@ static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
        return skb;
 }
 
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
+int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
 {
        struct sk_buff *skb;
        struct nfc_dev *dev;
@@ -630,26 +630,6 @@ int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
        return 0;
 }
 
-int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
-{
-       struct sk_buff *skb;
-       struct nfc_llcp_local *local;
-
-       pr_debug("Send DISC\n");
-
-       local = sock->local;
-       if (local == NULL)
-               return -ENODEV;
-
-       skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       skb_queue_head(&local->tx_queue, skb);
-
-       return 0;
-}
-
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
                          struct msghdr *msg, size_t len)
 {
index 158bdbf..81cd341 100644 (file)
@@ -537,6 +537,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        u8 *lto_tlv, lto_length;
        u8 *wks_tlv, wks_length;
        u8 *miux_tlv, miux_length;
+       __be16 wks = cpu_to_be16(local->local_wks);
        u8 gb_len = 0;
        int ret = 0;
 
@@ -549,8 +550,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        gb_len += lto_length;
 
        pr_debug("Local wks 0x%lx\n", local->local_wks);
-       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&local->local_wks, 2,
-                                    &wks_length);
+       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
        gb_len += wks_length;
 
        miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
@@ -719,6 +719,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                llcp_sock = nfc_llcp_sock(sk);
 
                if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+                       kfree_skb(skb);
+                       nfc_llcp_send_symm(local->dev);
+               } else if (llcp_sock && !llcp_sock->remote_ready) {
+                       skb_queue_head(&local->tx_queue, skb);
                        nfc_llcp_send_symm(local->dev);
                } else {
                        struct sk_buff *copy_skb = NULL;
@@ -730,6 +734,13 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       if (ptype == LLCP_PDU_DISC && sk != NULL &&
+                           sk->sk_state == LLCP_DISCONNECTING) {
+                               nfc_llcp_sock_unlink(&local->sockets, sk);
+                               sock_orphan(sk);
+                               sock_put(sk);
+                       }
+
                        if (ptype == LLCP_PDU_I)
                                copy_skb = skb_copy(skb, GFP_ATOMIC);
 
@@ -1579,6 +1590,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        local->lto = 150; /* 1500 ms */
        local->rw = LLCP_MAX_RW;
        local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+       local->local_wks = 0x1; /* LLC Link Management */
 
        nfc_llcp_build_gb(local);
 
index 380253e..d308402 100644 (file)
@@ -571,7 +571,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
        if (sk->sk_shutdown == SHUTDOWN_MASK)
                mask |= POLLHUP;
 
-       if (sock_writeable(sk))
+       if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED)
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
                set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -603,7 +603,7 @@ static int llcp_sock_release(struct socket *sock)
 
        /* Send a DISC */
        if (sk->sk_state == LLCP_CONNECTED)
-               nfc_llcp_disconnect(llcp_sock);
+               nfc_llcp_send_disconnect(llcp_sock);
 
        if (sk->sk_state == LLCP_LISTEN) {
                struct nfc_llcp_sock *lsk, *n;
@@ -614,7 +614,7 @@ static int llcp_sock_release(struct socket *sock)
                        accept_sk = &lsk->sk;
                        lock_sock(accept_sk);
 
-                       nfc_llcp_disconnect(lsk);
+                       nfc_llcp_send_disconnect(lsk);
                        nfc_llcp_accept_unlink(accept_sk);
 
                        release_sock(accept_sk);
@@ -626,6 +626,13 @@ static int llcp_sock_release(struct socket *sock)
 
        release_sock(sk);
 
+       /* Keep this sock alive and therefore do not remove it from the sockets
+        * list until the DISC PDU has been actually sent. Otherwise we would
+        * reply with DM PDUs before sending the DISC one.
+        */
+       if (sk->sk_state == LLCP_DISCONNECTING)
+               return err;
+
        if (sock->type == SOCK_RAW)
                nfc_llcp_sock_unlink(&local->raw_sockets, sk);
        else
@@ -722,14 +729,16 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
        if (ret)
                goto sock_unlink;
 
+       sk->sk_state = LLCP_CONNECTING;
+
        ret = sock_wait_state(sk, LLCP_CONNECTED,
                              sock_sndtimeo(sk, flags & O_NONBLOCK));
-       if (ret)
+       if (ret && ret != -EINPROGRESS)
                goto sock_unlink;
 
        release_sock(sk);
 
-       return 0;
+       return ret;
 
 sock_unlink:
        nfc_llcp_put_ssap(local, llcp_sock->ssap);
index 6d69b5f..2a24160 100644 (file)
@@ -8,3 +8,13 @@ config NFC_NCI
 
          Say Y here to compile NCI support into the kernel or say M to
          compile it as module (nci).
+
+config NFC_NCI_SPI
+       depends on NFC_NCI && SPI
+       bool "NCI over SPI protocol support"
+       default n
+       help
+         NCI (NFC Controller Interface) is a communication protocol between
+         an NFC Controller (NFCC) and a Device Host (DH).
+
+         Say yes if you use an NCI driver that requires SPI link layer.
index cdb3a2e..7aeedc4 100644 (file)
@@ -4,4 +4,6 @@
 
 obj-$(CONFIG_NFC_NCI) += nci.o
 
-nci-objs := core.o data.o lib.o ntf.o rsp.o
\ No newline at end of file
+nci-objs := core.o data.o lib.o ntf.o rsp.o
+
+nci-$(CONFIG_NFC_NCI_SPI) += spi.o
index 48ada0e..b943d46 100644 (file)
@@ -636,6 +636,21 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
        return rc;
 }
 
+static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_discover_se(struct nfc_dev *nfc_dev)
+{
+       return 0;
+}
+
 static struct nfc_ops nci_nfc_ops = {
        .dev_up = nci_dev_up,
        .dev_down = nci_dev_down,
@@ -646,6 +661,9 @@ static struct nfc_ops nci_nfc_ops = {
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
+       .enable_se = nci_enable_se,
+       .disable_se = nci_disable_se,
+       .discover_se = nci_discover_se,
 };
 
 /* ---- Interface to NCI drivers ---- */
@@ -658,7 +676,6 @@ static struct nfc_ops nci_nfc_ops = {
  */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nci_dev *ndev;
@@ -681,7 +698,6 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
 
        ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
                                            supported_protocols,
-                                           supported_se,
                                            tx_headroom + NCI_DATA_HDR_SIZE,
                                            tx_tailroom);
        if (!ndev->nfc_dev)
@@ -797,12 +813,11 @@ EXPORT_SYMBOL(nci_unregister_device);
 /**
  * nci_recv_frame - receive frame from NCI drivers
  *
+ * @ndev: The nci device
  * @skb: The sk_buff to receive
  */
-int nci_recv_frame(struct sk_buff *skb)
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
@@ -819,10 +834,8 @@ int nci_recv_frame(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(nci_recv_frame);
 
-static int nci_send_frame(struct sk_buff *skb)
+static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev) {
@@ -833,7 +846,7 @@ static int nci_send_frame(struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       return ndev->ops->send(skb);
+       return ndev->ops->send(ndev, skb);
 }
 
 /* Send NCI command */
@@ -861,8 +874,6 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
        if (plen)
                memcpy(skb_put(skb, plen), payload, plen);
 
-       skb->dev = (void *) ndev;
-
        skb_queue_tail(&ndev->cmd_q, skb);
        queue_work(ndev->cmd_wq, &ndev->cmd_work);
 
@@ -894,7 +905,7 @@ static void nci_tx_work(struct work_struct *work)
                         nci_conn_id(skb->data),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->data_timer,
                          jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
@@ -963,7 +974,7 @@ static void nci_cmd_work(struct work_struct *work)
                         nci_opcode_oid(nci_opcode(skb->data)),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->cmd_timer,
                          jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
index 76c48c5..2a9399d 100644 (file)
@@ -80,8 +80,6 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
 
        nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
        nci_pbf_set((__u8 *)hdr, pbf);
-
-       skb->dev = (void *) ndev;
 }
 
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
new file mode 100644 (file)
index 0000000..c7cf37b
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
+
+#include <linux/export.h>
+#include <linux/spi/spi.h>
+#include <linux/crc-ccitt.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#define NCI_SPI_HDR_LEN                        4
+#define NCI_SPI_CRC_LEN                        2
+#define NCI_SPI_ACK_SHIFT              6
+#define NCI_SPI_MSB_PAYLOAD_MASK       0x3F
+
+#define NCI_SPI_SEND_TIMEOUT   (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
+                                       NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
+
+#define NCI_SPI_DIRECT_WRITE   0x01
+#define NCI_SPI_DIRECT_READ    0x02
+
+#define ACKNOWLEDGE_NONE       0
+#define ACKNOWLEDGE_ACK                1
+#define ACKNOWLEDGE_NACK       2
+
+#define CRC_INIT               0xFFFF
+
+static int nci_spi_open(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->open(ndev);
+}
+
+static int nci_spi_close(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->close(ndev);
+}
+
+static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+{
+       struct spi_message m;
+       struct spi_transfer t;
+
+       t.tx_buf = skb->data;
+       t.len = skb->len;
+       t.cs_change = 0;
+       t.delay_usecs = ndev->xfer_udelay;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+
+       return spi_sync(ndev->spi, &m);
+}
+
+static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+       unsigned int payload_len = skb->len;
+       unsigned char *hdr;
+       int ret;
+       long completion_rc;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = ndev->acknowledge_mode;
+       hdr[2] = payload_len >> 8;
+       hdr[3] = payload_len & 0xFF;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               u16 crc;
+
+               crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+               *skb_put(skb, 1) = crc >> 8;
+               *skb_put(skb, 1) = crc & 0xFF;
+       }
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+       ndev->ops->assert_int(ndev);
+
+       if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+               goto done;
+
+       init_completion(&ndev->req_completion);
+       completion_rc =
+               wait_for_completion_interruptible_timeout(&ndev->req_completion,
+                                                         NCI_SPI_SEND_TIMEOUT);
+
+       if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+               ret = -EIO;
+
+done:
+       return ret;
+}
+
+static struct nci_ops nci_spi_ops = {
+       .open = nci_spi_open,
+       .close = nci_spi_close,
+       .send = nci_spi_send,
+};
+
+/* ---- Interface to NCI SPI drivers ---- */
+
+/**
+ * nci_spi_allocate_device - allocate a new nci spi device
+ *
+ * @spi: SPI device
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ * @supported_se: NFC Secure Elements supported by the device
+ * @acknowledge_mode: Acknowledge mode used by the device
+ * @delay: delay between transactions in us
+ */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay)
+{
+       struct nci_spi_dev *ndev;
+       int tailroom = 0;
+
+       if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+               return NULL;
+
+       if (!supported_protocols)
+               return NULL;
+
+       ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
+       if (!ndev)
+               return NULL;
+
+       ndev->ops = ops;
+       ndev->acknowledge_mode = acknowledge_mode;
+       ndev->xfer_udelay = delay;
+
+       if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               tailroom += NCI_SPI_CRC_LEN;
+
+       ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
+                                           NCI_SPI_HDR_LEN, tailroom);
+       if (!ndev->nci_dev)
+               return NULL;
+
+       nci_set_drvdata(ndev->nci_dev, ndev);
+
+       return ndev;
+}
+EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+
+/**
+ * nci_spi_free_device - deallocate nci spi device
+ *
+ * @ndev: The nci spi device to deallocate
+ */
+void nci_spi_free_device(struct nci_spi_dev *ndev)
+{
+       nci_free_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_free_device);
+
+/**
+ * nci_spi_register_device - register a nci spi device in the nfc subsystem
+ *
+ * @pdev: The nci spi device to register
+ */
+int nci_spi_register_device(struct nci_spi_dev *ndev)
+{
+       return nci_register_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_register_device);
+
+/**
+ * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
+ *
+ * @dev: The nci spi device to unregister
+ */
+void nci_spi_unregister_device(struct nci_spi_dev *ndev)
+{
+       nci_unregister_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
+
+static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+{
+       struct sk_buff *skb;
+       unsigned char *hdr;
+       u16 crc;
+       int ret;
+
+       skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = NCI_SPI_CRC_ENABLED;
+       hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
+       hdr[3] = 0;
+
+       crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+       *skb_put(skb, 1) = crc >> 8;
+       *skb_put(skb, 1) = crc & 0xFF;
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+
+       return ret;
+}
+
+static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       struct spi_message m;
+       unsigned char req[2], resp_hdr[2];
+       struct spi_transfer tx, rx;
+       unsigned short rx_len = 0;
+       int ret;
+
+       spi_message_init(&m);
+       req[0] = NCI_SPI_DIRECT_READ;
+       req[1] = ndev->acknowledge_mode;
+       tx.tx_buf = req;
+       tx.len = 2;
+       tx.cs_change = 0;
+       spi_message_add_tail(&tx, &m);
+       rx.rx_buf = resp_hdr;
+       rx.len = 2;
+       rx.cs_change = 1;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               return NULL;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
+                               resp_hdr[1] + NCI_SPI_CRC_LEN;
+       else
+               rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
+
+       skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       spi_message_init(&m);
+       rx.rx_buf = skb_put(skb, rx_len);
+       rx.len = rx_len;
+       rx.cs_change = 0;
+       rx.delay_usecs = ndev->xfer_udelay;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               goto receive_error;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               *skb_push(skb, 1) = resp_hdr[1];
+               *skb_push(skb, 1) = resp_hdr[0];
+       }
+
+       return skb;
+
+receive_error:
+       kfree_skb(skb);
+
+       return NULL;
+}
+
+static int nci_spi_check_crc(struct sk_buff *skb)
+{
+       u16 crc_data = (skb->data[skb->len - 2] << 8) |
+                       skb->data[skb->len - 1];
+       int ret;
+
+       ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
+                       == crc_data);
+
+       skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
+
+       return ret;
+}
+
+static u8 nci_spi_get_ack(struct sk_buff *skb)
+{
+       u8 ret;
+
+       ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
+
+       /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
+       skb_pull(skb, 2);
+
+       return ret;
+}
+
+/**
+ * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ *
+ * @ndev: The nci spi device
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep.  The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       int ret = 0;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* Retrieve frame from SPI */
+       skb = __nci_spi_recv_frame(ndev);
+       if (!skb) {
+               ret = -EIO;
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               if (!nci_spi_check_crc(skb)) {
+                       send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+                       goto done;
+               }
+
+               /* In case of acknowledged mode: if ACK or NACK received,
+                * unblock completion of latest frame sent.
+                */
+               ndev->req_result = nci_spi_get_ack(skb);
+               if (ndev->req_result)
+                       complete(&ndev->req_completion);
+       }
+
+       /* If there is no payload (ACK/NACK only frame),
+        * free the socket buffer
+        */
+       if (skb->len == 0) {
+               kfree_skb(skb);
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               send_acknowledge(ndev, ACKNOWLEDGE_ACK);
+
+       /* Forward skb to NCI core layer */
+       ret = nci_recv_frame(ndev->nci_dev, skb);
+
+done:
+       ndev->ops->assert_int(ndev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
index f0c4d61..b05ad90 100644 (file)
@@ -56,6 +56,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
        [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
        [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+       [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
+                                    .len = NFC_FIRMWARE_NAME_MAXSIZE },
 };
 
 static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -424,6 +426,69 @@ free_msg:
        return rc;
 }
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_ADDED);
+       if (!hdr)
+               goto free_msg;
+
+       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, type))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_REMOVED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
                                u32 portid, u32 seq,
                                struct netlink_callback *cb,
@@ -442,7 +507,6 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
-           nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
            nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
            nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
@@ -1025,6 +1089,108 @@ exit:
        return rc;
 }
 
+static int nfc_genl_fw_upload(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+       char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       nla_strlcpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME],
+                   sizeof(firmware_name));
+
+       rc = nfc_fw_upload(dev, firmware_name);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_CMD_FW_UPLOAD);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+           nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_enable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_disable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1084,6 +1250,21 @@ static struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_llc_sdreq,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_FW_UPLOAD,
+               .doit = nfc_genl_fw_upload,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_ENABLE_SE,
+               .doit = nfc_genl_enable_se,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_DISABLE_SE,
+               .doit = nfc_genl_disable_se,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index afa1f84..ee85a1f 100644 (file)
@@ -94,6 +94,9 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev);
 
 int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
@@ -120,6 +123,11 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
        class_dev_iter_exit(iter);
 }
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
 int nfc_dev_up(struct nfc_dev *dev);
 
 int nfc_dev_down(struct nfc_dev *dev);
@@ -139,4 +147,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
 int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                      data_exchange_cb_t cb, void *cb_context);
 
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx);
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __LOCAL_NFC_H */
index e4df774..f277246 100644 (file)
@@ -301,6 +301,9 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
                return NULL;
        }
 
+       /* atomic_inc_return makes it start at 1, make it start at 0 */
+       rdev->wiphy_idx--;
+
        /* give it a proper name */
        dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
 
@@ -449,8 +452,13 @@ int wiphy_register(struct wiphy *wiphy)
        u16 ifmodes = wiphy->interface_modes;
 
 #ifdef CONFIG_PM
-       if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
-                   !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+       if (WARN_ON(wiphy->wowlan &&
+                   (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+                   !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+               return -EINVAL;
+       if (WARN_ON(wiphy->wowlan &&
+                   !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns &&
+                   !wiphy->wowlan->tcp))
                return -EINVAL;
 #endif
 
@@ -540,25 +548,28 @@ int wiphy_register(struct wiphy *wiphy)
        }
 
 #ifdef CONFIG_PM
-       if (rdev->wiphy.wowlan.n_patterns) {
-               if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
-                           rdev->wiphy.wowlan.pattern_min_len >
-                           rdev->wiphy.wowlan.pattern_max_len))
-                       return -EINVAL;
-       }
+       if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns &&
+                   (!rdev->wiphy.wowlan->pattern_min_len ||
+                    rdev->wiphy.wowlan->pattern_min_len >
+                               rdev->wiphy.wowlan->pattern_max_len)))
+               return -EINVAL;
 #endif
 
        /* check and set up bitrates */
        ieee80211_set_bitrate_flags(wiphy);
 
-       rtnl_lock();
 
        res = device_add(&rdev->wiphy.dev);
+       if (res)
+               return res;
+
+       res = rfkill_register(rdev->rfkill);
        if (res) {
-               rtnl_unlock();
+               device_del(&rdev->wiphy.dev);
                return res;
        }
 
+       rtnl_lock();
        /* set up regulatory info */
        wiphy_regulatory_register(wiphy);
 
@@ -585,17 +596,6 @@ int wiphy_register(struct wiphy *wiphy)
 
        cfg80211_debugfs_rdev_add(rdev);
 
-       res = rfkill_register(rdev->rfkill);
-       if (res) {
-               device_del(&rdev->wiphy.dev);
-
-               debugfs_remove_recursive(rdev->wiphy.debugfsdir);
-               list_del_rcu(&rdev->list);
-               wiphy_regulatory_deregister(wiphy);
-               rtnl_unlock();
-               return res;
-       }
-
        rdev->wiphy.registered = true;
        rtnl_unlock();
        return 0;
@@ -632,11 +632,11 @@ void wiphy_unregister(struct wiphy *wiphy)
                rtnl_unlock();
                __count == 0; }));
 
+       rfkill_unregister(rdev->rfkill);
+
        rtnl_lock();
        rdev->wiphy.registered = false;
 
-       rfkill_unregister(rdev->rfkill);
-
        BUG_ON(!list_empty(&rdev->wdev_list));
 
        /*
@@ -816,7 +816,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        pr_err("failed to add phy80211 symlink to netdev!\n");
                }
                wdev->netdev = dev;
-               wdev->sme_state = CFG80211_SME_IDLE;
 #ifdef CONFIG_CFG80211_WEXT
                wdev->wext.default_key = -1;
                wdev->wext.default_mgmt_key = -1;
index a65eaf8..a6b45bf 100644 (file)
@@ -308,11 +308,6 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                           bool local_state_change);
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
-void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
-                              const u8 *req_ie, size_t req_ie_len,
-                              const u8 *resp_ie, size_t resp_ie_len,
-                              u16 status, bool wextev,
-                              struct cfg80211_bss *bss);
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
                                u16 frame_type, const u8 *match_data,
                                int match_len);
@@ -328,12 +323,19 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
 void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
                                const struct ieee80211_vht_cap *vht_capa_mask);
 
-/* SME */
+/* SME events */
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
                     struct cfg80211_connect_params *connect,
                     struct cfg80211_cached_keys *connkeys,
                     const u8 *prev_bssid);
+void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+                              const u8 *req_ie, size_t req_ie_len,
+                              const u8 *resp_ie, size_t resp_ie_len,
+                              u16 status, bool wextev,
+                              struct cfg80211_bss *bss);
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
+                            size_t ie_len, u16 reason, bool from_ap);
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, u16 reason,
                        bool wextev);
@@ -344,21 +346,21 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
 
+/* SME implementation */
 void cfg80211_conn_work(struct work_struct *work);
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev);
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev);
+void cfg80211_sme_scan_done(struct net_device *dev);
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
+void cfg80211_sme_disassoc(struct wireless_dev *wdev);
+void cfg80211_sme_deauth(struct wireless_dev *wdev);
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
 
 /* internal helpers */
 bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
 int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr);
-void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
-                            size_t ie_len, u16 reason, bool from_ap);
-void cfg80211_sme_scan_done(struct net_device *dev);
-void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss);
 void __cfg80211_scan_done(struct work_struct *wk);
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
 void __cfg80211_sched_scan_results(struct work_struct *wk);
index 5449c5a..39bff7d 100644 (file)
@@ -43,7 +43,6 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
@@ -64,8 +63,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
 
        trace_cfg80211_ibss_joined(dev, bssid);
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev), gfp);
        if (!ev)
                return;
@@ -120,7 +117,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
-       wdev->sme_state = CFG80211_SME_CONNECTING;
 
        err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
                                    params->channel_fixed
@@ -134,7 +130,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        err = rdev_join_ibss(rdev, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
-               wdev->sme_state = CFG80211_SME_IDLE;
                return err;
        }
 
@@ -186,7 +181,6 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 #ifdef CONFIG_CFG80211_WEXT
        if (!nowext)
index 5dfb289..30c4920 100644 (file)
@@ -18,6 +18,7 @@
 #define MESH_PATH_TO_ROOT_TIMEOUT      6000
 #define MESH_ROOT_INTERVAL     5000
 #define MESH_ROOT_CONFIRMATION_INTERVAL 2000
+#define MESH_DEFAULT_PLINK_TIMEOUT     1800 /* timeout in seconds */
 
 /*
  * Minimum interval between two consecutive PREQs originated by the same
@@ -75,6 +76,7 @@ const struct mesh_config default_mesh_config = {
        .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
        .power_mode = NL80211_MESH_POWER_ACTIVE,
        .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
+       .plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
 };
 
 const struct mesh_setup default_mesh_setup = {
@@ -160,6 +162,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
        }
 
+       /*
+        * check if basic rates are available otherwise use mandatory rates as
+        * basic rates
+        */
+       if (!setup->basic_rates) {
+               struct ieee80211_supported_band *sband =
+                               rdev->wiphy.bands[setup->chandef.chan->band];
+               setup->basic_rates = ieee80211_mandatory_rates(sband);
+       }
+
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
                return -EINVAL;
 
index 7bde5d9..a61a44b 100644 (file)
 #include "rdev-ops.h"
 
 
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_send_rx_auth(dev);
-
-       nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
-       cfg80211_sme_rx_auth(dev, buf, len);
-}
-EXPORT_SYMBOL(cfg80211_send_rx_auth);
-
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
                            const u8 *buf, size_t len)
 {
-       u16 status_code;
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u8 *ie = mgmt->u.assoc_resp.variable;
        int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+       u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 
        trace_cfg80211_send_rx_assoc(dev, bss);
 
-       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
-
        /*
         * This is a bit of a hack, we don't notify userspace of
         * a (re-)association reply if we tried to send a reassoc
         * and got a reject -- we only try again with an assoc
         * frame instead of reassoc.
         */
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
-           cfg80211_sme_failed_reassoc(wdev)) {
+       if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
                cfg80211_put_bss(wiphy, bss);
                return;
        }
 
        nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
-
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
-               cfg80211_sme_failed_assoc(wdev);
-               /*
-                * do not call connect_result() now because the
-                * sme will schedule work that does it later.
-                */
-               cfg80211_put_bss(wiphy, bss);
-               return;
-       }
-
-       if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
-               /*
-                * This is for the userspace SME, the CONNECTING
-                * state will be changed to CONNECTED by
-                * __cfg80211_connect_result() below.
-                */
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-       }
-
-       /* this consumes the bss reference */
+       /* update current_bss etc., consumes the bss reference */
        __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
                                  status_code,
                                  status_code == WLAN_STATUS_SUCCESS, bss);
 }
-EXPORT_SYMBOL(cfg80211_send_rx_assoc);
+EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 
-void cfg80211_send_deauth(struct net_device *dev,
-                         const u8 *buf, size_t len)
+static void cfg80211_process_auth(struct wireless_dev *wdev,
+                                 const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+       cfg80211_sme_rx_auth(wdev, buf, len);
+}
+
+static void cfg80211_process_deauth(struct wireless_dev *wdev,
+                                   const u8 *buf, size_t len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        const u8 *bssid = mgmt->bssid;
-       bool was_current = false;
+       u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       trace_cfg80211_send_deauth(dev);
-       ASSERT_WDEV_LOCK(wdev);
+       nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
 
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-               was_current = true;
-       }
+       if (!wdev->current_bss ||
+           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
+               return;
+
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_deauth(wdev);
+}
 
-       nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+static void cfg80211_process_disassoc(struct wireless_dev *wdev,
+                                     const u8 *buf, size_t len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+       const u8 *bssid = mgmt->bssid;
+       u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
-               u16 reason_code;
-               bool from_ap;
+       nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
 
-               reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       if (WARN_ON(!wdev->current_bss ||
+                   !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+               return;
 
-               from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-               __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
-       }
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_disassoc(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_deauth);
 
-void cfg80211_send_disassoc(struct net_device *dev,
-                           const u8 *buf, size_t len)
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
-       const u8 *bssid = mgmt->bssid;
-       u16 reason_code;
-       bool from_ap;
+       struct ieee80211_mgmt *mgmt = (void *)buf;
 
-       trace_cfg80211_send_disassoc(dev);
        ASSERT_WDEV_LOCK(wdev);
 
-       nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
+       trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
+       if (WARN_ON(len < 2))
                return;
 
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_sme_disassoc(dev, wdev->current_bss);
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       } else
-               WARN_ON(1);
-
-
-       reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
-
-       from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-       __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
+       if (ieee80211_is_auth(mgmt->frame_control))
+               cfg80211_process_auth(wdev, buf, len);
+       else if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else if (ieee80211_is_disassoc(mgmt->frame_control))
+               cfg80211_process_disassoc(wdev, buf, len);
 }
-EXPORT_SYMBOL(cfg80211_send_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
 
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
@@ -170,14 +127,11 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
        trace_cfg80211_send_auth_timeout(dev, addr);
 
        nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       cfg80211_sme_auth_timeout(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_auth_timeout);
+EXPORT_SYMBOL(cfg80211_auth_timeout);
 
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
@@ -186,12 +140,28 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
        trace_cfg80211_send_assoc_timeout(dev, addr);
 
        nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       cfg80211_sme_assoc_timeout(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
+EXPORT_SYMBOL(cfg80211_assoc_timeout);
+
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_mgmt *mgmt = (void *)buf;
+
+       ASSERT_WDEV_LOCK(wdev);
+
+       trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
+
+       if (WARN_ON(len < 2))
+               return;
+
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else
+               cfg80211_process_disassoc(wdev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
 
 void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
                                  enum nl80211_key_type key_type, int key_id,
@@ -314,21 +284,12 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
-       bool was_connected = false;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->current_bss && req->prev_bssid &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
-               /*
-                * Trying to reassociate: Allow this to proceed and let the old
-                * association to be dropped when the new one is completed.
-                */
-               if (wdev->sme_state == CFG80211_SME_CONNECTED) {
-                       was_connected = true;
-                       wdev->sme_state = CFG80211_SME_CONNECTING;
-               }
-       } else if (wdev->current_bss)
+       if (wdev->current_bss &&
+           (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
+                                                  req->prev_bssid)))
                return -EALREADY;
 
        cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
@@ -338,11 +299,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 
        req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       if (!req->bss) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (!req->bss)
                return -ENOENT;
-       }
 
        err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
        if (err)
@@ -351,11 +309,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
        err = rdev_assoc(rdev, dev, req);
 
 out:
-       if (err) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (err)
                cfg80211_put_bss(&rdev->wiphy, req->bss);
-       }
 
        return err;
 }
@@ -376,8 +331,9 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (local_state_change && (!wdev->current_bss ||
-           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+       if (local_state_change &&
+           (!wdev->current_bss ||
+            !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
                return 0;
 
        return rdev_deauth(rdev, dev, &req);
@@ -395,13 +351,11 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                .ie = ie,
                .ie_len = ie_len,
        };
+       int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return -ENOTCONN;
-
-       if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
+       if (!wdev->current_bss)
                return -ENOTCONN;
 
        if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
@@ -409,7 +363,13 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
        else
                return -ENOTCONN;
 
-       return rdev_disassoc(rdev, dev, &req);
+       err = rdev_disassoc(rdev, dev, &req);
+       if (err)
+               return err;
+
+       /* driver should have reported the disassoc */
+       WARN_ON(wdev->current_bss);
+       return 0;
 }
 
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
@@ -417,10 +377,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        u8 bssid[ETH_ALEN];
-       struct cfg80211_deauth_request req = {
-               .reason_code = WLAN_REASON_DEAUTH_LEAVING,
-               .bssid = bssid,
-       };
 
        ASSERT_WDEV_LOCK(wdev);
 
@@ -431,13 +387,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                return;
 
        memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
-       rdev_deauth(rdev, dev, &req);
-
-       if (wdev->current_bss) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       }
+       cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+                            WLAN_REASON_DEAUTH_LEAVING, false);
 }
 
 struct cfg80211_mgmt_registration {
index ea74b9d..e545023 100644 (file)
@@ -800,12 +800,9 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_MESH_POINT:
                break;
        case NL80211_IFTYPE_ADHOC:
-               if (!wdev->current_bss)
-                       return -ENOLINK;
-               break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+               if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        default:
@@ -908,7 +905,7 @@ nla_put_failure:
 static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
                                        struct sk_buff *msg)
 {
-       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
        struct nlattr *nl_tcp;
 
        if (!tcp)
@@ -951,37 +948,37 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+       if (!dev->wiphy.wowlan)
                return 0;
 
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
 
-       if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+       if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
 
-       if (dev->wiphy.wowlan.n_patterns) {
+       if (dev->wiphy.wowlan->n_patterns) {
                struct nl80211_wowlan_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,
-                       .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+                       .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,
+                       .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -3986,10 +3983,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        params.listen_interval =
                nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
 
-       if (info->attrs[NL80211_ATTR_STA_AID])
-               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
-       else
+       if (info->attrs[NL80211_ATTR_PEER_AID])
                params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+       else
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
        if (!params.aid || params.aid > IEEE80211_MAX_AID)
                return -EINVAL;
 
@@ -4041,7 +4038,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
@@ -4067,7 +4065,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
@@ -4589,7 +4588,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
                        cur_params.power_mode) ||
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
-                       cur_params.dot11MeshAwakeWindowDuration))
+                       cur_params.dot11MeshAwakeWindowDuration) ||
+           nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       cur_params.plink_timeout))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -4630,6 +4631,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
 };
 
 static const struct nla_policy
@@ -4767,6 +4769,9 @@ do {                                                                          \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+                                 mask, NL80211_MESHCONF_PLINK_TIMEOUT,
+                                 nla_get_u32);
        if (mask_out)
                *mask_out = mask;
 
@@ -7153,6 +7158,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+                       return -EINVAL;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -7160,7 +7168,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_P2P_DEVICE:
                break;
        default:
                return -EOPNOTSUPP;
@@ -7188,9 +7195,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
-       err = nl80211_parse_chandef(rdev, info, &chandef);
-       if (err)
-               return err;
+       /* get the channel if any has been specified, otherwise pass NULL to
+        * the driver. The latter will use the current one
+        */
+       chandef.chan = NULL;
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               err = nl80211_parse_chandef(rdev, info, &chandef);
+               if (err)
+                       return err;
+       }
+
+       if (!chandef.chan && offchan)
+               return -EINVAL;
 
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7495,6 +7511,23 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                setup.chandef.chan = NULL;
        }
 
+       if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+               u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               int n_rates =
+                       nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               struct ieee80211_supported_band *sband;
+
+               if (!setup.chandef.chan)
+                       return -EINVAL;
+
+               sband = rdev->wiphy.bands[setup.chandef.chan->band];
+
+               err = ieee80211_get_ratemask(sband, rates, n_rates,
+                                            &setup.basic_rates);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -7591,8 +7624,7 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        void *hdr;
        u32 size = NLMSG_DEFAULT_SIZE;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan)
                return -EOPNOTSUPP;
 
        if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
@@ -7665,7 +7697,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
        int err, port;
 
-       if (!rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan->tcp)
                return -EINVAL;
 
        err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
@@ -7685,16 +7717,16 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
-       if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
+       if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+                       rdev->wiphy.wowlan->tcp->data_interval_max ||
            nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
-       if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
+       if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
                return -EINVAL;
 
        wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
@@ -7709,13 +7741,13 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
                if (!tok->len || tokens_size % tok->len)
                        return -EINVAL;
-               if (!rdev->wiphy.wowlan.tcp->tok)
+               if (!rdev->wiphy.wowlan->tcp->tok)
                        return -EINVAL;
-               if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
+               if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
                        return -EINVAL;
-               if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
+               if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
                        return -EINVAL;
-               if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
+               if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
                        return -EINVAL;
                if (tok->offset + tok->len > data_size)
                        return -EINVAL;
@@ -7723,7 +7755,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
        if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
                seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
-               if (!rdev->wiphy.wowlan.tcp->seq)
+               if (!rdev->wiphy.wowlan->tcp->seq)
                        return -EINVAL;
                if (seq->len == 0 || seq->len > 4)
                        return -EINVAL;
@@ -7804,12 +7836,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
-       struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
+       const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
        bool prev_enabled = rdev->wiphy.wowlan_config;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!wowlan)
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
@@ -9326,31 +9357,27 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len)
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
+                                 size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       const struct ieee80211_mgmt *mgmt = (void *)buf;
+       u32 cmd;
 
-       trace_cfg80211_send_unprot_deauth(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
+       if (WARN_ON(len < 2))
+               return;
 
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
+       else
+               cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
 
-       trace_cfg80211_send_unprot_disassoc(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
+       trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
+       nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
 }
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9861,7 +9888,6 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
 
        if (!nlportid)
@@ -9882,12 +9908,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return true;
-       }
-
+       genlmsg_end(msg, hdr);
        genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
        return true;
 
@@ -10330,10 +10351,7 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10399,7 +10417,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_probe_status(dev, addr, cookie, acked);
 
@@ -10421,11 +10438,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
            (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10491,7 +10504,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err, size = 200;
+       int size = 200;
 
        trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
 
@@ -10577,9 +10590,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                nla_nest_end(msg, reasons);
        }
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0)
-               goto free_msg;
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10599,7 +10610,6 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
                                         reason_code);
@@ -10622,11 +10632,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
             nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10684,7 +10690,6 @@ void cfg80211_ft_event(struct net_device *netdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_ft_event(wiphy, netdev, ft_event);
 
@@ -10710,11 +10715,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
                        ft_event->ric_ies);
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);
index e1d6749..5a24c98 100644 (file)
@@ -1345,7 +1345,7 @@ get_reg_request_treatment(struct wiphy *wiphy,
                                return REG_REQ_OK;
                        return REG_REQ_ALREADY_SET;
                }
-               return 0;
+               return REG_REQ_OK;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
                        if (regdom_changes(pending_request->alpha2))
index 81be95f..ae7e2cb 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * SME code for cfg80211's connect emulation.
+ * SME code for cfg80211
+ * both driver SME event handling and the SME implementation
+ * (for nl80211's connect() and wext)
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
  * Copyright (C) 2009   Intel Corporation. All rights reserved.
 #include "reg.h"
 #include "rdev-ops.h"
 
+/*
+ * Software SME in cfg80211, using auth/assoc/deauth calls to the
+ * driver. This is is for implementing nl80211's connect/disconnect
+ * and wireless extensions (if configured.)
+ */
+
 struct cfg80211_conn {
        struct cfg80211_connect_params params;
        /* these are sub-states of the _CONNECTING sme_state */
        enum {
-               CFG80211_CONN_IDLE,
                CFG80211_CONN_SCANNING,
                CFG80211_CONN_SCAN_AGAIN,
                CFG80211_CONN_AUTHENTICATE_NEXT,
                CFG80211_CONN_AUTHENTICATING,
                CFG80211_CONN_ASSOCIATE_NEXT,
                CFG80211_CONN_ASSOCIATING,
-               CFG80211_CONN_DEAUTH_ASSOC_FAIL,
+               CFG80211_CONN_DEAUTH,
+               CFG80211_CONN_CONNECTED,
        } state;
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
        u8 *ie;
@@ -37,39 +45,16 @@ struct cfg80211_conn {
        bool auto_auth, prev_bssid_valid;
 };
 
-static bool cfg80211_is_all_idle(void)
+static void cfg80211_sme_free(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev;
-       struct wireless_dev *wdev;
-       bool is_all_idle = true;
-
-       /*
-        * All devices must be idle as otherwise if you are actively
-        * scanning some new beacon hints could be learned and would
-        * count as new regulatory hints.
-        */
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
-               list_for_each_entry(wdev, &rdev->wdev_list, list) {
-                       wdev_lock(wdev);
-                       if (wdev->sme_state != CFG80211_SME_IDLE)
-                               is_all_idle = false;
-                       wdev_unlock(wdev);
-               }
-       }
-
-       return is_all_idle;
-}
+       if (!wdev->conn)
+               return;
 
-static void disconnect_work(struct work_struct *work)
-{
-       rtnl_lock();
-       if (cfg80211_is_all_idle())
-               regulatory_hint_disconnect();
-       rtnl_unlock();
+       kfree(wdev->conn->ie);
+       kfree(wdev->conn);
+       wdev->conn = NULL;
 }
 
-static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
-
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -164,6 +149,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        params = &wdev->conn->params;
 
        switch (wdev->conn->state) {
+       case CFG80211_CONN_SCANNING:
+               /* didn't find it during scan ... */
+               return -ENOENT;
        case CFG80211_CONN_SCAN_AGAIN:
                return cfg80211_conn_scan(wdev);
        case CFG80211_CONN_AUTHENTICATE_NEXT:
@@ -200,12 +188,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                             WLAN_REASON_DEAUTH_LEAVING,
                                             false);
                return err;
-       case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
+       case CFG80211_CONN_DEAUTH:
                cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                     NULL, 0,
                                     WLAN_REASON_DEAUTH_LEAVING, false);
-               /* return an error so that we call __cfg80211_connect_result() */
-               return -EINVAL;
+               return 0;
        default:
                return 0;
        }
@@ -229,7 +216,8 @@ void cfg80211_conn_work(struct work_struct *work)
                        wdev_unlock(wdev);
                        continue;
                }
-               if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) {
+               if (!wdev->conn ||
+                   wdev->conn->state == CFG80211_CONN_CONNECTED) {
                        wdev_unlock(wdev);
                        continue;
                }
@@ -237,12 +225,14 @@ void cfg80211_conn_work(struct work_struct *work)
                        memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
                        bssid = bssid_buf;
                }
-               if (cfg80211_conn_do_work(wdev))
+               if (cfg80211_conn_do_work(wdev)) {
                        __cfg80211_connect_result(
                                        wdev->netdev, bssid,
                                        NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
                                        false, NULL);
+                       cfg80211_sme_free(wdev);
+               }
                wdev_unlock(wdev);
        }
 
@@ -286,9 +276,6 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        if (!wdev->conn)
                return;
 
@@ -297,20 +284,10 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
                return;
 
        bss = cfg80211_get_conn_bss(wdev);
-       if (bss) {
+       if (bss)
                cfg80211_put_bss(&rdev->wiphy, bss);
-       } else {
-               /* not found */
-               if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
-                       schedule_work(&rdev->conn_work);
-               else
-                       __cfg80211_connect_result(
-                                       wdev->netdev,
-                                       wdev->conn->params.bssid,
-                                       NULL, 0, NULL, 0,
-                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                       false, NULL);
-       }
+       else
+               schedule_work(&rdev->conn_work);
 }
 
 void cfg80211_sme_scan_done(struct net_device *dev)
@@ -322,10 +299,8 @@ void cfg80211_sme_scan_done(struct net_device *dev)
        wdev_unlock(wdev);
 }
 
-void cfg80211_sme_rx_auth(struct net_device *dev,
-                         const u8 *buf, size_t len)
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
@@ -333,11 +308,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       /* should only RX auth frames when connecting */
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
                return;
 
        if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
@@ -366,46 +337,226 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
                schedule_work(&rdev->conn_work);
        } else if (status_code != WLAN_STATUS_SUCCESS) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
+               __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
+                                         NULL, 0, NULL, 0,
                                          status_code, false, NULL);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
+       } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
                wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
                schedule_work(&rdev->conn_work);
        }
 }
 
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn)
                return false;
 
-       if (!wdev->conn->prev_bssid_valid)
+       if (status == WLAN_STATUS_SUCCESS) {
+               wdev->conn->state = CFG80211_CONN_CONNECTED;
                return false;
+       }
 
-       /*
-        * Some stupid APs don't accept reassoc, so we
-        * need to fall back to trying regular assoc.
-        */
-       wdev->conn->prev_bssid_valid = false;
-       wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+       if (wdev->conn->prev_bssid_valid) {
+               /*
+                * Some stupid APs don't accept reassoc, so we
+                * need to fall back to trying regular assoc;
+                * return true so no event is sent to userspace.
+                */
+               wdev->conn->prev_bssid_valid = false;
+               wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+               schedule_work(&rdev->conn_work);
+               return true;
+       }
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
+       return false;
+}
 
-       return true;
+void cfg80211_sme_deauth(struct wireless_dev *wdev)
+{
+       cfg80211_sme_free(wdev);
 }
 
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       cfg80211_sme_free(wdev);
+}
 
-       wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
+void cfg80211_sme_disassoc(struct wireless_dev *wdev)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       if (!wdev->conn)
+               return;
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
 }
 
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
+{
+       cfg80211_sme_disassoc(wdev);
+}
+
+static int cfg80211_sme_connect(struct wireless_dev *wdev,
+                               struct cfg80211_connect_params *connect,
+                               const u8 *prev_bssid)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_bss *bss;
+       int err;
+
+       if (!rdev->ops->auth || !rdev->ops->assoc)
+               return -EOPNOTSUPP;
+
+       if (wdev->current_bss)
+               return -EALREADY;
+
+       if (WARN_ON(wdev->conn))
+               return -EINPROGRESS;
+
+       wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
+       if (!wdev->conn)
+               return -ENOMEM;
+
+       /*
+        * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
+        */
+       memcpy(&wdev->conn->params, connect, sizeof(*connect));
+       if (connect->bssid) {
+               wdev->conn->params.bssid = wdev->conn->bssid;
+               memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
+       }
+
+       if (connect->ie) {
+               wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
+                                       GFP_KERNEL);
+               wdev->conn->params.ie = wdev->conn->ie;
+               if (!wdev->conn->ie) {
+                       kfree(wdev->conn);
+                       wdev->conn = NULL;
+                       return -ENOMEM;
+               }
+       }
+
+       if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+               wdev->conn->auto_auth = true;
+               /* start with open system ... should mostly work */
+               wdev->conn->params.auth_type =
+                       NL80211_AUTHTYPE_OPEN_SYSTEM;
+       } else {
+               wdev->conn->auto_auth = false;
+       }
+
+       wdev->conn->params.ssid = wdev->ssid;
+       wdev->conn->params.ssid_len = connect->ssid_len;
+
+       /* see if we have the bss already */
+       bss = cfg80211_get_conn_bss(wdev);
+
+       if (prev_bssid) {
+               memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
+               wdev->conn->prev_bssid_valid = true;
+       }
+
+       /* we're good if we have a matching bss struct */
+       if (bss) {
+               wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
+               err = cfg80211_conn_do_work(wdev);
+               cfg80211_put_bss(wdev->wiphy, bss);
+       } else {
+               /* otherwise we'll need to scan for the AP first */
+               err = cfg80211_conn_scan(wdev);
+
+               /*
+                * If we can't scan right now, then we need to scan again
+                * after the current scan finished, since the parameters
+                * changed (unless we find a good AP anyway).
+                */
+               if (err == -EBUSY) {
+                       err = 0;
+                       wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
+               }
+       }
+
+       if (err)
+               cfg80211_sme_free(wdev);
+
+       return err;
+}
+
+static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       int err;
+
+       if (!wdev->conn)
+               return 0;
+
+       if (!rdev->ops->deauth)
+               return -EOPNOTSUPP;
+
+       if (wdev->conn->state == CFG80211_CONN_SCANNING ||
+           wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
+               err = 0;
+               goto out;
+       }
+
+       /* wdev->conn->params.bssid must be set if > SCANNING */
+       err = cfg80211_mlme_deauth(rdev, wdev->netdev,
+                                  wdev->conn->params.bssid,
+                                  NULL, 0, reason, false);
+ out:
+       cfg80211_sme_free(wdev);
+       return err;
+}
+
+/*
+ * code shared for in-device and software SME
+ */
+
+static bool cfg80211_is_all_idle(void)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       bool is_all_idle = true;
+
+       /*
+        * All devices must be idle as otherwise if you are actively
+        * scanning some new beacon hints could be learned and would
+        * count as new regulatory hints.
+        */
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               list_for_each_entry(wdev, &rdev->wdev_list, list) {
+                       wdev_lock(wdev);
+                       if (wdev->conn || wdev->current_bss)
+                               is_all_idle = false;
+                       wdev_unlock(wdev);
+               }
+       }
+
+       return is_all_idle;
+}
+
+static void disconnect_work(struct work_struct *work)
+{
+       rtnl_lock();
+       if (cfg80211_is_all_idle())
+               regulatory_hint_disconnect();
+       rtnl_unlock();
+}
+
+static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+
+
+/*
+ * API calls for drivers implementing connect/disconnect and
+ * SME event handling
+ */
+
 void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
@@ -424,9 +575,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
                                    resp_ie, resp_ie_len,
@@ -463,15 +611,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                wdev->current_bss = NULL;
        }
 
-       if (wdev->conn)
-               wdev->conn->state = CFG80211_CONN_IDLE;
-
        if (status != WLAN_STATUS_SUCCESS) {
-               wdev->sme_state = CFG80211_SME_IDLE;
-               if (wdev->conn)
-                       kfree(wdev->conn->ie);
-               kfree(wdev->conn);
-               wdev->conn = NULL;
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
                wdev->ssid_len = 0;
@@ -480,21 +620,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        }
 
        if (!bss)
-               bss = cfg80211_get_bss(wdev->wiphy,
-                                      wdev->conn ? wdev->conn->params.channel :
-                                      NULL,
-                                      bssid,
+               bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
                                       WLAN_CAPABILITY_ESS,
                                       WLAN_CAPABILITY_ESS);
-
        if (WARN_ON(!bss))
                return;
 
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        rcu_read_lock();
@@ -530,8 +665,6 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
        if (!ev)
                return;
@@ -572,13 +705,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                goto out;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               goto out;
-
-       /* internal error -- how did we get to CONNECTED w/o BSS? */
-       if (WARN_ON(!wdev->current_bss)) {
+       if (WARN_ON(!wdev->current_bss))
                goto out;
-       }
 
        cfg80211_unhold_bss(wdev->current_bss);
        cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
@@ -628,8 +756,6 @@ void cfg80211_roamed(struct net_device *dev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_bss *bss;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
                               wdev->ssid_len, WLAN_CAPABILITY_ESS,
                               WLAN_CAPABILITY_ESS);
@@ -651,8 +777,6 @@ void cfg80211_roamed_bss(struct net_device *dev,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        if (WARN_ON(!bss))
                return;
 
@@ -694,25 +818,14 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return;
-
        if (wdev->current_bss) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 
-       if (wdev->conn) {
-               kfree(wdev->conn->ie);
-               wdev->conn->ie = NULL;
-               kfree(wdev->conn);
-               wdev->conn = NULL;
-       }
-
        nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
 
        /*
@@ -741,8 +854,6 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        ev = kzalloc(sizeof(*ev) + ie_len, gfp);
        if (!ev)
                return;
@@ -760,6 +871,9 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
 }
 EXPORT_SYMBOL(cfg80211_disconnected);
 
+/*
+ * API calls for nl80211/wext compatibility code
+ */
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
                     struct cfg80211_connect_params *connect,
@@ -767,14 +881,10 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     const u8 *prev_bssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_bss *bss = NULL;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE)
-               return -EALREADY;
-
        if (WARN_ON(wdev->connect_keys)) {
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
@@ -810,105 +920,22 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                }
        }
 
-       if (!rdev->ops->connect) {
-               if (!rdev->ops->auth || !rdev->ops->assoc)
-                       return -EOPNOTSUPP;
-
-               if (WARN_ON(wdev->conn))
-                       return -EINPROGRESS;
-
-               wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
-               if (!wdev->conn)
-                       return -ENOMEM;
-
-               /*
-                * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
-                */
-               memcpy(&wdev->conn->params, connect, sizeof(*connect));
-               if (connect->bssid) {
-                       wdev->conn->params.bssid = wdev->conn->bssid;
-                       memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
-               }
+       wdev->connect_keys = connkeys;
+       memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+       wdev->ssid_len = connect->ssid_len;
 
-               if (connect->ie) {
-                       wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-                                               GFP_KERNEL);
-                       wdev->conn->params.ie = wdev->conn->ie;
-                       if (!wdev->conn->ie) {
-                               kfree(wdev->conn);
-                               wdev->conn = NULL;
-                               return -ENOMEM;
-                       }
-               }
-
-               if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
-                       wdev->conn->auto_auth = true;
-                       /* start with open system ... should mostly work */
-                       wdev->conn->params.auth_type =
-                               NL80211_AUTHTYPE_OPEN_SYSTEM;
-               } else {
-                       wdev->conn->auto_auth = false;
-               }
-
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
-               wdev->conn->params.ssid = wdev->ssid;
-               wdev->conn->params.ssid_len = connect->ssid_len;
-
-               /* see if we have the bss already */
-               bss = cfg80211_get_conn_bss(wdev);
-
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
-
-               if (prev_bssid) {
-                       memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
-                       wdev->conn->prev_bssid_valid = true;
-               }
-
-               /* we're good if we have a matching bss struct */
-               if (bss) {
-                       wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
-                       err = cfg80211_conn_do_work(wdev);
-                       cfg80211_put_bss(wdev->wiphy, bss);
-               } else {
-                       /* otherwise we'll need to scan for the AP first */
-                       err = cfg80211_conn_scan(wdev);
-                       /*
-                        * If we can't scan right now, then we need to scan again
-                        * after the current scan finished, since the parameters
-                        * changed (unless we find a good AP anyway).
-                        */
-                       if (err == -EBUSY) {
-                               err = 0;
-                               wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
-                       }
-               }
-               if (err) {
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       wdev->connect_keys = NULL;
-                       wdev->ssid_len = 0;
-               }
-
-               return err;
-       } else {
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
+       if (!rdev->ops->connect)
+               err = cfg80211_sme_connect(wdev, connect, prev_bssid);
+       else
                err = rdev_connect(rdev, dev, connect);
-               if (err) {
-                       wdev->connect_keys = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       return err;
-               }
 
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
-
-               return 0;
+       if (err) {
+               wdev->connect_keys = NULL;
+               wdev->ssid_len = 0;
+               return err;
        }
+
+       return 0;
 }
 
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
@@ -919,78 +946,17 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state == CFG80211_SME_IDLE)
-               return -EINVAL;
-
        kfree(wdev->connect_keys);
        wdev->connect_keys = NULL;
 
-       if (!rdev->ops->disconnect) {
-               if (!rdev->ops->deauth)
-                       return -EOPNOTSUPP;
-
-               /* was it connected by userspace SME? */
-               if (!wdev->conn) {
-                       cfg80211_mlme_down(rdev, dev);
-                       goto disconnect;
-               }
-
-               if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                   (wdev->conn->state == CFG80211_CONN_SCANNING ||
-                    wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->ssid_len = 0;
-                       return 0;
-               }
-
-               /* wdev->conn->params.bssid must be set if > SCANNING */
-               err = cfg80211_mlme_deauth(rdev, dev,
-                                            wdev->conn->params.bssid,
-                                            NULL, 0, reason, false);
-               if (err)
-                       return err;
+       if (wdev->conn) {
+               err = cfg80211_sme_disconnect(wdev, reason);
+       } else if (!rdev->ops->disconnect) {
+               cfg80211_mlme_down(rdev, dev);
+               err = 0;
        } else {
                err = rdev_disconnect(rdev, dev, reason);
-               if (err)
-                       return err;
        }
 
- disconnect:
-       if (wdev->sme_state == CFG80211_SME_CONNECTED)
-               __cfg80211_disconnected(dev, NULL, 0, 0, false);
-       else if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         wextev, NULL);
-
-       return 0;
-}
-
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
-       u8 bssid[ETH_ALEN];
-
-       ASSERT_WDEV_LOCK(wdev);
-
-       if (!wdev->conn)
-               return;
-
-       if (wdev->conn->state == CFG80211_CONN_IDLE)
-               return;
-
-       /*
-        * Ok, so the association was made by this SME -- we don't
-        * want it any more so deauthenticate too.
-        */
-
-       memcpy(bssid, bss->pub.bssid, ETH_ALEN);
-
-       cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
-                            WLAN_REASON_DEAUTH_LEAVING, false);
+       return err;
 }
index 23fafea..e1534ba 100644 (file)
@@ -1911,24 +1911,46 @@ TRACE_EVENT(cfg80211_send_rx_assoc,
                  NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DECLARE_EVENT_CLASS(netdev_frame_event,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_unprot_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+TRACE_EVENT(cfg80211_tx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
 DECLARE_EVENT_CLASS(netdev_mac_evt,
index a53f840..14c9a25 100644 (file)
@@ -89,7 +89,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.channel == chan) {
@@ -188,7 +188,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 
        err = 0;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.ssid && len &&
@@ -277,7 +277,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                err = 0;
                /* both automatic */
                if (!bssid && !wdev->wext.connect.bssid)
@@ -364,7 +364,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
        wdev->wext.ie = ie;
        wdev->wext.ie_len = ie_len;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                err = cfg80211_disconnect(rdev, dev,
                                          WLAN_REASON_DEAUTH_LEAVING, false);
                if (err)